roojs-core.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
64         isTouch =  'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123         
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618         /** @type Boolean */
619         isTouch : isTouch,
620
621         /**
622          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
623          * you may want to set this to true.
624          * @type Boolean
625          */
626         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
627         
628         
629                 
630         /**
631          * Selects a single element as a Roo Element
632          * This is about as close as you can get to jQuery's $('do crazy stuff')
633          * @param {String} selector The selector/xpath query
634          * @param {Node} root (optional) The start of the query (defaults to document).
635          * @return {Roo.Element}
636          */
637         selectNode : function(selector, root) 
638         {
639             var node = Roo.DomQuery.selectNode(selector,root);
640             return node ? Roo.get(node) : new Roo.Element(false);
641         }
642         
643     });
644
645
646 })();
647
648 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
649                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
650                 "Roo.app", "Roo.ux",
651                 "Roo.bootstrap",
652                 "Roo.bootstrap.dash");
653 /*
654  * Based on:
655  * Ext JS Library 1.1.1
656  * Copyright(c) 2006-2007, Ext JS, LLC.
657  *
658  * Originally Released Under LGPL - original licence link has changed is not relivant.
659  *
660  * Fork - LGPL
661  * <script type="text/javascript">
662  */
663
664 (function() {    
665     // wrappedn so fnCleanup is not in global scope...
666     if(Roo.isIE) {
667         function fnCleanUp() {
668             var p = Function.prototype;
669             delete p.createSequence;
670             delete p.defer;
671             delete p.createDelegate;
672             delete p.createCallback;
673             delete p.createInterceptor;
674
675             window.detachEvent("onunload", fnCleanUp);
676         }
677         window.attachEvent("onunload", fnCleanUp);
678     }
679 })();
680
681
682 /**
683  * @class Function
684  * These functions are available on every Function object (any JavaScript function).
685  */
686 Roo.apply(Function.prototype, {
687      /**
688      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
689      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
690      * Will create a function that is bound to those 2 args.
691      * @return {Function} The new function
692     */
693     createCallback : function(/*args...*/){
694         // make args available, in function below
695         var args = arguments;
696         var method = this;
697         return function() {
698             return method.apply(window, args);
699         };
700     },
701
702     /**
703      * Creates a delegate (callback) that sets the scope to obj.
704      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
705      * Will create a function that is automatically scoped to this.
706      * @param {Object} obj (optional) The object for which the scope is set
707      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
708      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
709      *                                             if a number the args are inserted at the specified position
710      * @return {Function} The new function
711      */
712     createDelegate : function(obj, args, appendArgs){
713         var method = this;
714         return function() {
715             var callArgs = args || arguments;
716             if(appendArgs === true){
717                 callArgs = Array.prototype.slice.call(arguments, 0);
718                 callArgs = callArgs.concat(args);
719             }else if(typeof appendArgs == "number"){
720                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
721                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
722                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
723             }
724             return method.apply(obj || window, callArgs);
725         };
726     },
727
728     /**
729      * Calls this function after the number of millseconds specified.
730      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
731      * @param {Object} obj (optional) The object for which the scope is set
732      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
733      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
734      *                                             if a number the args are inserted at the specified position
735      * @return {Number} The timeout id that can be used with clearTimeout
736      */
737     defer : function(millis, obj, args, appendArgs){
738         var fn = this.createDelegate(obj, args, appendArgs);
739         if(millis){
740             return setTimeout(fn, millis);
741         }
742         fn();
743         return 0;
744     },
745     /**
746      * Create a combined function call sequence of the original function + the passed function.
747      * The resulting function returns the results of the original function.
748      * The passed fcn is called with the parameters of the original function
749      * @param {Function} fcn The function to sequence
750      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
751      * @return {Function} The new function
752      */
753     createSequence : function(fcn, scope){
754         if(typeof fcn != "function"){
755             return this;
756         }
757         var method = this;
758         return function() {
759             var retval = method.apply(this || window, arguments);
760             fcn.apply(scope || this || window, arguments);
761             return retval;
762         };
763     },
764
765     /**
766      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
767      * The resulting function returns the results of the original function.
768      * The passed fcn is called with the parameters of the original function.
769      * @addon
770      * @param {Function} fcn The function to call before the original
771      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
772      * @return {Function} The new function
773      */
774     createInterceptor : function(fcn, scope){
775         if(typeof fcn != "function"){
776             return this;
777         }
778         var method = this;
779         return function() {
780             fcn.target = this;
781             fcn.method = method;
782             if(fcn.apply(scope || this || window, arguments) === false){
783                 return;
784             }
785             return method.apply(this || window, arguments);
786         };
787     }
788 });
789 /*
790  * Based on:
791  * Ext JS Library 1.1.1
792  * Copyright(c) 2006-2007, Ext JS, LLC.
793  *
794  * Originally Released Under LGPL - original licence link has changed is not relivant.
795  *
796  * Fork - LGPL
797  * <script type="text/javascript">
798  */
799
800 Roo.applyIf(String, {
801     
802     /** @scope String */
803     
804     /**
805      * Escapes the passed string for ' and \
806      * @param {String} string The string to escape
807      * @return {String} The escaped string
808      * @static
809      */
810     escape : function(string) {
811         return string.replace(/('|\\)/g, "\\$1");
812     },
813
814     /**
815      * Pads the left side of a string with a specified character.  This is especially useful
816      * for normalizing number and date strings.  Example usage:
817      * <pre><code>
818 var s = String.leftPad('123', 5, '0');
819 // s now contains the string: '00123'
820 </code></pre>
821      * @param {String} string The original string
822      * @param {Number} size The total length of the output string
823      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
824      * @return {String} The padded string
825      * @static
826      */
827     leftPad : function (val, size, ch) {
828         var result = new String(val);
829         if(ch === null || ch === undefined || ch === '') {
830             ch = " ";
831         }
832         while (result.length < size) {
833             result = ch + result;
834         }
835         return result;
836     },
837
838     /**
839      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
840      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
841      * <pre><code>
842 var cls = 'my-class', text = 'Some text';
843 var s = String.format('<div class="{0}">{1}</div>', cls, text);
844 // s now contains the string: '<div class="my-class">Some text</div>'
845 </code></pre>
846      * @param {String} string The tokenized string to be formatted
847      * @param {String} value1 The value to replace token {0}
848      * @param {String} value2 Etc...
849      * @return {String} The formatted string
850      * @static
851      */
852     format : function(format){
853         var args = Array.prototype.slice.call(arguments, 1);
854         return format.replace(/\{(\d+)\}/g, function(m, i){
855             return Roo.util.Format.htmlEncode(args[i]);
856         });
857     }
858 });
859
860 /**
861  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
862  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
863  * they are already different, the first value passed in is returned.  Note that this method returns the new value
864  * but does not change the current string.
865  * <pre><code>
866 // alternate sort directions
867 sort = sort.toggle('ASC', 'DESC');
868
869 // instead of conditional logic:
870 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
871 </code></pre>
872  * @param {String} value The value to compare to the current string
873  * @param {String} other The new value to use if the string already equals the first value passed in
874  * @return {String} The new value
875  */
876  
877 String.prototype.toggle = function(value, other){
878     return this == value ? other : value;
879 };/*
880  * Based on:
881  * Ext JS Library 1.1.1
882  * Copyright(c) 2006-2007, Ext JS, LLC.
883  *
884  * Originally Released Under LGPL - original licence link has changed is not relivant.
885  *
886  * Fork - LGPL
887  * <script type="text/javascript">
888  */
889
890  /**
891  * @class Number
892  */
893 Roo.applyIf(Number.prototype, {
894     /**
895      * Checks whether or not the current number is within a desired range.  If the number is already within the
896      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
897      * exceeded.  Note that this method returns the constrained value but does not change the current number.
898      * @param {Number} min The minimum number in the range
899      * @param {Number} max The maximum number in the range
900      * @return {Number} The constrained value if outside the range, otherwise the current value
901      */
902     constrain : function(min, max){
903         return Math.min(Math.max(this, min), max);
904     }
905 });/*
906  * Based on:
907  * Ext JS Library 1.1.1
908  * Copyright(c) 2006-2007, Ext JS, LLC.
909  *
910  * Originally Released Under LGPL - original licence link has changed is not relivant.
911  *
912  * Fork - LGPL
913  * <script type="text/javascript">
914  */
915  /**
916  * @class Array
917  */
918 Roo.applyIf(Array.prototype, {
919     /**
920      * Checks whether or not the specified object exists in the array.
921      * @param {Object} o The object to check for
922      * @return {Number} The index of o in the array (or -1 if it is not found)
923      */
924     indexOf : function(o){
925        for (var i = 0, len = this.length; i < len; i++){
926               if(this[i] == o) return i;
927        }
928            return -1;
929     },
930
931     /**
932      * Removes the specified object from the array.  If the object is not found nothing happens.
933      * @param {Object} o The object to remove
934      */
935     remove : function(o){
936        var index = this.indexOf(o);
937        if(index != -1){
938            this.splice(index, 1);
939        }
940     },
941     /**
942      * Map (JS 1.6 compatibility)
943      * @param {Function} function  to call
944      */
945     map : function(fun )
946     {
947         var len = this.length >>> 0;
948         if (typeof fun != "function")
949             throw new TypeError();
950
951         var res = new Array(len);
952         var thisp = arguments[1];
953         for (var i = 0; i < len; i++)
954         {
955             if (i in this)
956                 res[i] = fun.call(thisp, this[i], i, this);
957         }
958
959         return res;
960     }
961     
962 });
963
964
965  /*
966  * Based on:
967  * Ext JS Library 1.1.1
968  * Copyright(c) 2006-2007, Ext JS, LLC.
969  *
970  * Originally Released Under LGPL - original licence link has changed is not relivant.
971  *
972  * Fork - LGPL
973  * <script type="text/javascript">
974  */
975
976 /**
977  * @class Date
978  *
979  * The date parsing and format syntax is a subset of
980  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
981  * supported will provide results equivalent to their PHP versions.
982  *
983  * Following is the list of all currently supported formats:
984  *<pre>
985 Sample date:
986 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
987
988 Format  Output      Description
989 ------  ----------  --------------------------------------------------------------
990   d      10         Day of the month, 2 digits with leading zeros
991   D      Wed        A textual representation of a day, three letters
992   j      10         Day of the month without leading zeros
993   l      Wednesday  A full textual representation of the day of the week
994   S      th         English ordinal day of month suffix, 2 chars (use with j)
995   w      3          Numeric representation of the day of the week
996   z      9          The julian date, or day of the year (0-365)
997   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
998   F      January    A full textual representation of the month
999   m      01         Numeric representation of a month, with leading zeros
1000   M      Jan        Month name abbreviation, three letters
1001   n      1          Numeric representation of a month, without leading zeros
1002   t      31         Number of days in the given month
1003   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1004   Y      2007       A full numeric representation of a year, 4 digits
1005   y      07         A two digit representation of a year
1006   a      pm         Lowercase Ante meridiem and Post meridiem
1007   A      PM         Uppercase Ante meridiem and Post meridiem
1008   g      3          12-hour format of an hour without leading zeros
1009   G      15         24-hour format of an hour without leading zeros
1010   h      03         12-hour format of an hour with leading zeros
1011   H      15         24-hour format of an hour with leading zeros
1012   i      05         Minutes with leading zeros
1013   s      01         Seconds, with leading zeros
1014   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1015   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1016   T      CST        Timezone setting of the machine running the code
1017   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1018 </pre>
1019  *
1020  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1021  * <pre><code>
1022 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1023 document.write(dt.format('Y-m-d'));                         //2007-01-10
1024 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1025 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1026  </code></pre>
1027  *
1028  * Here are some standard date/time patterns that you might find helpful.  They
1029  * are not part of the source of Date.js, but to use them you can simply copy this
1030  * block of code into any script that is included after Date.js and they will also become
1031  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1032  * <pre><code>
1033 Date.patterns = {
1034     ISO8601Long:"Y-m-d H:i:s",
1035     ISO8601Short:"Y-m-d",
1036     ShortDate: "n/j/Y",
1037     LongDate: "l, F d, Y",
1038     FullDateTime: "l, F d, Y g:i:s A",
1039     MonthDay: "F d",
1040     ShortTime: "g:i A",
1041     LongTime: "g:i:s A",
1042     SortableDateTime: "Y-m-d\\TH:i:s",
1043     UniversalSortableDateTime: "Y-m-d H:i:sO",
1044     YearMonth: "F, Y"
1045 };
1046 </code></pre>
1047  *
1048  * Example usage:
1049  * <pre><code>
1050 var dt = new Date();
1051 document.write(dt.format(Date.patterns.ShortDate));
1052  </code></pre>
1053  */
1054
1055 /*
1056  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1057  * They generate precompiled functions from date formats instead of parsing and
1058  * processing the pattern every time you format a date.  These functions are available
1059  * on every Date object (any javascript function).
1060  *
1061  * The original article and download are here:
1062  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1063  *
1064  */
1065  
1066  
1067  // was in core
1068 /**
1069  Returns the number of milliseconds between this date and date
1070  @param {Date} date (optional) Defaults to now
1071  @return {Number} The diff in milliseconds
1072  @member Date getElapsed
1073  */
1074 Date.prototype.getElapsed = function(date) {
1075         return Math.abs((date || new Date()).getTime()-this.getTime());
1076 };
1077 // was in date file..
1078
1079
1080 // private
1081 Date.parseFunctions = {count:0};
1082 // private
1083 Date.parseRegexes = [];
1084 // private
1085 Date.formatFunctions = {count:0};
1086
1087 // private
1088 Date.prototype.dateFormat = function(format) {
1089     if (Date.formatFunctions[format] == null) {
1090         Date.createNewFormat(format);
1091     }
1092     var func = Date.formatFunctions[format];
1093     return this[func]();
1094 };
1095
1096
1097 /**
1098  * Formats a date given the supplied format string
1099  * @param {String} format The format string
1100  * @return {String} The formatted date
1101  * @method
1102  */
1103 Date.prototype.format = Date.prototype.dateFormat;
1104
1105 // private
1106 Date.createNewFormat = function(format) {
1107     var funcName = "format" + Date.formatFunctions.count++;
1108     Date.formatFunctions[format] = funcName;
1109     var code = "Date.prototype." + funcName + " = function(){return ";
1110     var special = false;
1111     var ch = '';
1112     for (var i = 0; i < format.length; ++i) {
1113         ch = format.charAt(i);
1114         if (!special && ch == "\\") {
1115             special = true;
1116         }
1117         else if (special) {
1118             special = false;
1119             code += "'" + String.escape(ch) + "' + ";
1120         }
1121         else {
1122             code += Date.getFormatCode(ch);
1123         }
1124     }
1125     /** eval:var:zzzzzzzzzzzzz */
1126     eval(code.substring(0, code.length - 3) + ";}");
1127 };
1128
1129 // private
1130 Date.getFormatCode = function(character) {
1131     switch (character) {
1132     case "d":
1133         return "String.leftPad(this.getDate(), 2, '0') + ";
1134     case "D":
1135         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1136     case "j":
1137         return "this.getDate() + ";
1138     case "l":
1139         return "Date.dayNames[this.getDay()] + ";
1140     case "S":
1141         return "this.getSuffix() + ";
1142     case "w":
1143         return "this.getDay() + ";
1144     case "z":
1145         return "this.getDayOfYear() + ";
1146     case "W":
1147         return "this.getWeekOfYear() + ";
1148     case "F":
1149         return "Date.monthNames[this.getMonth()] + ";
1150     case "m":
1151         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1152     case "M":
1153         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1154     case "n":
1155         return "(this.getMonth() + 1) + ";
1156     case "t":
1157         return "this.getDaysInMonth() + ";
1158     case "L":
1159         return "(this.isLeapYear() ? 1 : 0) + ";
1160     case "Y":
1161         return "this.getFullYear() + ";
1162     case "y":
1163         return "('' + this.getFullYear()).substring(2, 4) + ";
1164     case "a":
1165         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1166     case "A":
1167         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1168     case "g":
1169         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1170     case "G":
1171         return "this.getHours() + ";
1172     case "h":
1173         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1174     case "H":
1175         return "String.leftPad(this.getHours(), 2, '0') + ";
1176     case "i":
1177         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1178     case "s":
1179         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1180     case "O":
1181         return "this.getGMTOffset() + ";
1182     case "P":
1183         return "this.getGMTColonOffset() + ";
1184     case "T":
1185         return "this.getTimezone() + ";
1186     case "Z":
1187         return "(this.getTimezoneOffset() * -60) + ";
1188     default:
1189         return "'" + String.escape(character) + "' + ";
1190     }
1191 };
1192
1193 /**
1194  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1195  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1196  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1197  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1198  * string or the parse operation will fail.
1199  * Example Usage:
1200 <pre><code>
1201 //dt = Fri May 25 2007 (current date)
1202 var dt = new Date();
1203
1204 //dt = Thu May 25 2006 (today's month/day in 2006)
1205 dt = Date.parseDate("2006", "Y");
1206
1207 //dt = Sun Jan 15 2006 (all date parts specified)
1208 dt = Date.parseDate("2006-1-15", "Y-m-d");
1209
1210 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1211 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1212 </code></pre>
1213  * @param {String} input The unparsed date as a string
1214  * @param {String} format The format the date is in
1215  * @return {Date} The parsed date
1216  * @static
1217  */
1218 Date.parseDate = function(input, format) {
1219     if (Date.parseFunctions[format] == null) {
1220         Date.createParser(format);
1221     }
1222     var func = Date.parseFunctions[format];
1223     return Date[func](input);
1224 };
1225 /**
1226  * @private
1227  */
1228 Date.createParser = function(format) {
1229     var funcName = "parse" + Date.parseFunctions.count++;
1230     var regexNum = Date.parseRegexes.length;
1231     var currentGroup = 1;
1232     Date.parseFunctions[format] = funcName;
1233
1234     var code = "Date." + funcName + " = function(input){\n"
1235         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1236         + "var d = new Date();\n"
1237         + "y = d.getFullYear();\n"
1238         + "m = d.getMonth();\n"
1239         + "d = d.getDate();\n"
1240         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1241         + "if (results && results.length > 0) {";
1242     var regex = "";
1243
1244     var special = false;
1245     var ch = '';
1246     for (var i = 0; i < format.length; ++i) {
1247         ch = format.charAt(i);
1248         if (!special && ch == "\\") {
1249             special = true;
1250         }
1251         else if (special) {
1252             special = false;
1253             regex += String.escape(ch);
1254         }
1255         else {
1256             var obj = Date.formatCodeToRegex(ch, currentGroup);
1257             currentGroup += obj.g;
1258             regex += obj.s;
1259             if (obj.g && obj.c) {
1260                 code += obj.c;
1261             }
1262         }
1263     }
1264
1265     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1266         + "{v = new Date(y, m, d, h, i, s);}\n"
1267         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1268         + "{v = new Date(y, m, d, h, i);}\n"
1269         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1270         + "{v = new Date(y, m, d, h);}\n"
1271         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1272         + "{v = new Date(y, m, d);}\n"
1273         + "else if (y >= 0 && m >= 0)\n"
1274         + "{v = new Date(y, m);}\n"
1275         + "else if (y >= 0)\n"
1276         + "{v = new Date(y);}\n"
1277         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1278         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1279         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1280         + ";}";
1281
1282     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1283     /** eval:var:zzzzzzzzzzzzz */
1284     eval(code);
1285 };
1286
1287 // private
1288 Date.formatCodeToRegex = function(character, currentGroup) {
1289     switch (character) {
1290     case "D":
1291         return {g:0,
1292         c:null,
1293         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1294     case "j":
1295         return {g:1,
1296             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1297             s:"(\\d{1,2})"}; // day of month without leading zeroes
1298     case "d":
1299         return {g:1,
1300             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1301             s:"(\\d{2})"}; // day of month with leading zeroes
1302     case "l":
1303         return {g:0,
1304             c:null,
1305             s:"(?:" + Date.dayNames.join("|") + ")"};
1306     case "S":
1307         return {g:0,
1308             c:null,
1309             s:"(?:st|nd|rd|th)"};
1310     case "w":
1311         return {g:0,
1312             c:null,
1313             s:"\\d"};
1314     case "z":
1315         return {g:0,
1316             c:null,
1317             s:"(?:\\d{1,3})"};
1318     case "W":
1319         return {g:0,
1320             c:null,
1321             s:"(?:\\d{2})"};
1322     case "F":
1323         return {g:1,
1324             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1325             s:"(" + Date.monthNames.join("|") + ")"};
1326     case "M":
1327         return {g:1,
1328             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1329             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1330     case "n":
1331         return {g:1,
1332             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1333             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1334     case "m":
1335         return {g:1,
1336             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1337             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1338     case "t":
1339         return {g:0,
1340             c:null,
1341             s:"\\d{1,2}"};
1342     case "L":
1343         return {g:0,
1344             c:null,
1345             s:"(?:1|0)"};
1346     case "Y":
1347         return {g:1,
1348             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1349             s:"(\\d{4})"};
1350     case "y":
1351         return {g:1,
1352             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1353                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1354             s:"(\\d{1,2})"};
1355     case "a":
1356         return {g:1,
1357             c:"if (results[" + currentGroup + "] == 'am') {\n"
1358                 + "if (h == 12) { h = 0; }\n"
1359                 + "} else { if (h < 12) { h += 12; }}",
1360             s:"(am|pm)"};
1361     case "A":
1362         return {g:1,
1363             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1364                 + "if (h == 12) { h = 0; }\n"
1365                 + "} else { if (h < 12) { h += 12; }}",
1366             s:"(AM|PM)"};
1367     case "g":
1368     case "G":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1372     case "h":
1373     case "H":
1374         return {g:1,
1375             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1376             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1377     case "i":
1378         return {g:1,
1379             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1380             s:"(\\d{2})"};
1381     case "s":
1382         return {g:1,
1383             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1384             s:"(\\d{2})"};
1385     case "O":
1386         return {g:1,
1387             c:[
1388                 "o = results[", currentGroup, "];\n",
1389                 "var sn = o.substring(0,1);\n", // get + / - sign
1390                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1391                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1392                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1393                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1394             ].join(""),
1395             s:"([+\-]\\d{2,4})"};
1396     
1397     
1398     case "P":
1399         return {g:1,
1400                 c:[
1401                    "o = results[", currentGroup, "];\n",
1402                    "var sn = o.substring(0,1);\n",
1403                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1404                    "var mn = o.substring(4,6) % 60;\n",
1405                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1406                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1407             ].join(""),
1408             s:"([+\-]\\d{4})"};
1409     case "T":
1410         return {g:0,
1411             c:null,
1412             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1413     case "Z":
1414         return {g:1,
1415             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1416                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1417             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1418     default:
1419         return {g:0,
1420             c:null,
1421             s:String.escape(character)};
1422     }
1423 };
1424
1425 /**
1426  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1427  * @return {String} The abbreviated timezone name (e.g. 'CST')
1428  */
1429 Date.prototype.getTimezone = function() {
1430     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1431 };
1432
1433 /**
1434  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1435  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1436  */
1437 Date.prototype.getGMTOffset = function() {
1438     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1439         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1440         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1441 };
1442
1443 /**
1444  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1445  * @return {String} 2-characters representing hours and 2-characters representing minutes
1446  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1447  */
1448 Date.prototype.getGMTColonOffset = function() {
1449         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1450                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1451                 + ":"
1452                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1453 }
1454
1455 /**
1456  * Get the numeric day number of the year, adjusted for leap year.
1457  * @return {Number} 0 through 364 (365 in leap years)
1458  */
1459 Date.prototype.getDayOfYear = function() {
1460     var num = 0;
1461     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1462     for (var i = 0; i < this.getMonth(); ++i) {
1463         num += Date.daysInMonth[i];
1464     }
1465     return num + this.getDate() - 1;
1466 };
1467
1468 /**
1469  * Get the string representation of the numeric week number of the year
1470  * (equivalent to the format specifier 'W').
1471  * @return {String} '00' through '52'
1472  */
1473 Date.prototype.getWeekOfYear = function() {
1474     // Skip to Thursday of this week
1475     var now = this.getDayOfYear() + (4 - this.getDay());
1476     // Find the first Thursday of the year
1477     var jan1 = new Date(this.getFullYear(), 0, 1);
1478     var then = (7 - jan1.getDay() + 4);
1479     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1480 };
1481
1482 /**
1483  * Whether or not the current date is in a leap year.
1484  * @return {Boolean} True if the current date is in a leap year, else false
1485  */
1486 Date.prototype.isLeapYear = function() {
1487     var year = this.getFullYear();
1488     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1489 };
1490
1491 /**
1492  * Get the first day of the current month, adjusted for leap year.  The returned value
1493  * is the numeric day index within the week (0-6) which can be used in conjunction with
1494  * the {@link #monthNames} array to retrieve the textual day name.
1495  * Example:
1496  *<pre><code>
1497 var dt = new Date('1/10/2007');
1498 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1499 </code></pre>
1500  * @return {Number} The day number (0-6)
1501  */
1502 Date.prototype.getFirstDayOfMonth = function() {
1503     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1504     return (day < 0) ? (day + 7) : day;
1505 };
1506
1507 /**
1508  * Get the last day of the current month, adjusted for leap year.  The returned value
1509  * is the numeric day index within the week (0-6) which can be used in conjunction with
1510  * the {@link #monthNames} array to retrieve the textual day name.
1511  * Example:
1512  *<pre><code>
1513 var dt = new Date('1/10/2007');
1514 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1515 </code></pre>
1516  * @return {Number} The day number (0-6)
1517  */
1518 Date.prototype.getLastDayOfMonth = function() {
1519     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1520     return (day < 0) ? (day + 7) : day;
1521 };
1522
1523
1524 /**
1525  * Get the first date of this date's month
1526  * @return {Date}
1527  */
1528 Date.prototype.getFirstDateOfMonth = function() {
1529     return new Date(this.getFullYear(), this.getMonth(), 1);
1530 };
1531
1532 /**
1533  * Get the last date of this date's month
1534  * @return {Date}
1535  */
1536 Date.prototype.getLastDateOfMonth = function() {
1537     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1538 };
1539 /**
1540  * Get the number of days in the current month, adjusted for leap year.
1541  * @return {Number} The number of days in the month
1542  */
1543 Date.prototype.getDaysInMonth = function() {
1544     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1545     return Date.daysInMonth[this.getMonth()];
1546 };
1547
1548 /**
1549  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1550  * @return {String} 'st, 'nd', 'rd' or 'th'
1551  */
1552 Date.prototype.getSuffix = function() {
1553     switch (this.getDate()) {
1554         case 1:
1555         case 21:
1556         case 31:
1557             return "st";
1558         case 2:
1559         case 22:
1560             return "nd";
1561         case 3:
1562         case 23:
1563             return "rd";
1564         default:
1565             return "th";
1566     }
1567 };
1568
1569 // private
1570 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1571
1572 /**
1573  * An array of textual month names.
1574  * Override these values for international dates, for example...
1575  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1576  * @type Array
1577  * @static
1578  */
1579 Date.monthNames =
1580    ["January",
1581     "February",
1582     "March",
1583     "April",
1584     "May",
1585     "June",
1586     "July",
1587     "August",
1588     "September",
1589     "October",
1590     "November",
1591     "December"];
1592
1593 /**
1594  * An array of textual day names.
1595  * Override these values for international dates, for example...
1596  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1597  * @type Array
1598  * @static
1599  */
1600 Date.dayNames =
1601    ["Sunday",
1602     "Monday",
1603     "Tuesday",
1604     "Wednesday",
1605     "Thursday",
1606     "Friday",
1607     "Saturday"];
1608
1609 // private
1610 Date.y2kYear = 50;
1611 // private
1612 Date.monthNumbers = {
1613     Jan:0,
1614     Feb:1,
1615     Mar:2,
1616     Apr:3,
1617     May:4,
1618     Jun:5,
1619     Jul:6,
1620     Aug:7,
1621     Sep:8,
1622     Oct:9,
1623     Nov:10,
1624     Dec:11};
1625
1626 /**
1627  * Creates and returns a new Date instance with the exact same date value as the called instance.
1628  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1629  * variable will also be changed.  When the intention is to create a new variable that will not
1630  * modify the original instance, you should create a clone.
1631  *
1632  * Example of correctly cloning a date:
1633  * <pre><code>
1634 //wrong way:
1635 var orig = new Date('10/1/2006');
1636 var copy = orig;
1637 copy.setDate(5);
1638 document.write(orig);  //returns 'Thu Oct 05 2006'!
1639
1640 //correct way:
1641 var orig = new Date('10/1/2006');
1642 var copy = orig.clone();
1643 copy.setDate(5);
1644 document.write(orig);  //returns 'Thu Oct 01 2006'
1645 </code></pre>
1646  * @return {Date} The new Date instance
1647  */
1648 Date.prototype.clone = function() {
1649         return new Date(this.getTime());
1650 };
1651
1652 /**
1653  * Clears any time information from this date
1654  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1655  @return {Date} this or the clone
1656  */
1657 Date.prototype.clearTime = function(clone){
1658     if(clone){
1659         return this.clone().clearTime();
1660     }
1661     this.setHours(0);
1662     this.setMinutes(0);
1663     this.setSeconds(0);
1664     this.setMilliseconds(0);
1665     return this;
1666 };
1667
1668 // private
1669 // safari setMonth is broken
1670 if(Roo.isSafari){
1671     Date.brokenSetMonth = Date.prototype.setMonth;
1672         Date.prototype.setMonth = function(num){
1673                 if(num <= -1){
1674                         var n = Math.ceil(-num);
1675                         var back_year = Math.ceil(n/12);
1676                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1677                         this.setFullYear(this.getFullYear() - back_year);
1678                         return Date.brokenSetMonth.call(this, month);
1679                 } else {
1680                         return Date.brokenSetMonth.apply(this, arguments);
1681                 }
1682         };
1683 }
1684
1685 /** Date interval constant 
1686 * @static 
1687 * @type String */
1688 Date.MILLI = "ms";
1689 /** Date interval constant 
1690 * @static 
1691 * @type String */
1692 Date.SECOND = "s";
1693 /** Date interval constant 
1694 * @static 
1695 * @type String */
1696 Date.MINUTE = "mi";
1697 /** Date interval constant 
1698 * @static 
1699 * @type String */
1700 Date.HOUR = "h";
1701 /** Date interval constant 
1702 * @static 
1703 * @type String */
1704 Date.DAY = "d";
1705 /** Date interval constant 
1706 * @static 
1707 * @type String */
1708 Date.MONTH = "mo";
1709 /** Date interval constant 
1710 * @static 
1711 * @type String */
1712 Date.YEAR = "y";
1713
1714 /**
1715  * Provides a convenient method of performing basic date arithmetic.  This method
1716  * does not modify the Date instance being called - it creates and returns
1717  * a new Date instance containing the resulting date value.
1718  *
1719  * Examples:
1720  * <pre><code>
1721 //Basic usage:
1722 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1723 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1724
1725 //Negative values will subtract correctly:
1726 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1727 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1728
1729 //You can even chain several calls together in one line!
1730 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1731 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1732  </code></pre>
1733  *
1734  * @param {String} interval   A valid date interval enum value
1735  * @param {Number} value      The amount to add to the current date
1736  * @return {Date} The new Date instance
1737  */
1738 Date.prototype.add = function(interval, value){
1739   var d = this.clone();
1740   if (!interval || value === 0) return d;
1741   switch(interval.toLowerCase()){
1742     case Date.MILLI:
1743       d.setMilliseconds(this.getMilliseconds() + value);
1744       break;
1745     case Date.SECOND:
1746       d.setSeconds(this.getSeconds() + value);
1747       break;
1748     case Date.MINUTE:
1749       d.setMinutes(this.getMinutes() + value);
1750       break;
1751     case Date.HOUR:
1752       d.setHours(this.getHours() + value);
1753       break;
1754     case Date.DAY:
1755       d.setDate(this.getDate() + value);
1756       break;
1757     case Date.MONTH:
1758       var day = this.getDate();
1759       if(day > 28){
1760           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1761       }
1762       d.setDate(day);
1763       d.setMonth(this.getMonth() + value);
1764       break;
1765     case Date.YEAR:
1766       d.setFullYear(this.getFullYear() + value);
1767       break;
1768   }
1769   return d;
1770 };
1771 /*
1772  * Based on:
1773  * Ext JS Library 1.1.1
1774  * Copyright(c) 2006-2007, Ext JS, LLC.
1775  *
1776  * Originally Released Under LGPL - original licence link has changed is not relivant.
1777  *
1778  * Fork - LGPL
1779  * <script type="text/javascript">
1780  */
1781
1782 /**
1783  * @class Roo.lib.Dom
1784  * @static
1785  * 
1786  * Dom utils (from YIU afaik)
1787  * 
1788  **/
1789 Roo.lib.Dom = {
1790     /**
1791      * Get the view width
1792      * @param {Boolean} full True will get the full document, otherwise it's the view width
1793      * @return {Number} The width
1794      */
1795      
1796     getViewWidth : function(full) {
1797         return full ? this.getDocumentWidth() : this.getViewportWidth();
1798     },
1799     /**
1800      * Get the view height
1801      * @param {Boolean} full True will get the full document, otherwise it's the view height
1802      * @return {Number} The height
1803      */
1804     getViewHeight : function(full) {
1805         return full ? this.getDocumentHeight() : this.getViewportHeight();
1806     },
1807
1808     getDocumentHeight: function() {
1809         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1810         return Math.max(scrollHeight, this.getViewportHeight());
1811     },
1812
1813     getDocumentWidth: function() {
1814         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1815         return Math.max(scrollWidth, this.getViewportWidth());
1816     },
1817
1818     getViewportHeight: function() {
1819         var height = self.innerHeight;
1820         var mode = document.compatMode;
1821
1822         if ((mode || Roo.isIE) && !Roo.isOpera) {
1823             height = (mode == "CSS1Compat") ?
1824                      document.documentElement.clientHeight :
1825                      document.body.clientHeight;
1826         }
1827
1828         return height;
1829     },
1830
1831     getViewportWidth: function() {
1832         var width = self.innerWidth;
1833         var mode = document.compatMode;
1834
1835         if (mode || Roo.isIE) {
1836             width = (mode == "CSS1Compat") ?
1837                     document.documentElement.clientWidth :
1838                     document.body.clientWidth;
1839         }
1840         return width;
1841     },
1842
1843     isAncestor : function(p, c) {
1844         p = Roo.getDom(p);
1845         c = Roo.getDom(c);
1846         if (!p || !c) {
1847             return false;
1848         }
1849
1850         if (p.contains && !Roo.isSafari) {
1851             return p.contains(c);
1852         } else if (p.compareDocumentPosition) {
1853             return !!(p.compareDocumentPosition(c) & 16);
1854         } else {
1855             var parent = c.parentNode;
1856             while (parent) {
1857                 if (parent == p) {
1858                     return true;
1859                 }
1860                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1861                     return false;
1862                 }
1863                 parent = parent.parentNode;
1864             }
1865             return false;
1866         }
1867     },
1868
1869     getRegion : function(el) {
1870         return Roo.lib.Region.getRegion(el);
1871     },
1872
1873     getY : function(el) {
1874         return this.getXY(el)[1];
1875     },
1876
1877     getX : function(el) {
1878         return this.getXY(el)[0];
1879     },
1880
1881     getXY : function(el) {
1882         var p, pe, b, scroll, bd = document.body;
1883         el = Roo.getDom(el);
1884         var fly = Roo.lib.AnimBase.fly;
1885         if (el.getBoundingClientRect) {
1886             b = el.getBoundingClientRect();
1887             scroll = fly(document).getScroll();
1888             return [b.left + scroll.left, b.top + scroll.top];
1889         }
1890         var x = 0, y = 0;
1891
1892         p = el;
1893
1894         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1895
1896         while (p) {
1897
1898             x += p.offsetLeft;
1899             y += p.offsetTop;
1900
1901             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1902                 hasAbsolute = true;
1903             }
1904
1905             if (Roo.isGecko) {
1906                 pe = fly(p);
1907
1908                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1909                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1910
1911
1912                 x += bl;
1913                 y += bt;
1914
1915
1916                 if (p != el && pe.getStyle('overflow') != 'visible') {
1917                     x += bl;
1918                     y += bt;
1919                 }
1920             }
1921             p = p.offsetParent;
1922         }
1923
1924         if (Roo.isSafari && hasAbsolute) {
1925             x -= bd.offsetLeft;
1926             y -= bd.offsetTop;
1927         }
1928
1929         if (Roo.isGecko && !hasAbsolute) {
1930             var dbd = fly(bd);
1931             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1932             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1933         }
1934
1935         p = el.parentNode;
1936         while (p && p != bd) {
1937             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1938                 x -= p.scrollLeft;
1939                 y -= p.scrollTop;
1940             }
1941             p = p.parentNode;
1942         }
1943         return [x, y];
1944     },
1945  
1946   
1947
1948
1949     setXY : function(el, xy) {
1950         el = Roo.fly(el, '_setXY');
1951         el.position();
1952         var pts = el.translatePoints(xy);
1953         if (xy[0] !== false) {
1954             el.dom.style.left = pts.left + "px";
1955         }
1956         if (xy[1] !== false) {
1957             el.dom.style.top = pts.top + "px";
1958         }
1959     },
1960
1961     setX : function(el, x) {
1962         this.setXY(el, [x, false]);
1963     },
1964
1965     setY : function(el, y) {
1966         this.setXY(el, [false, y]);
1967     }
1968 };
1969 /*
1970  * Portions of this file are based on pieces of Yahoo User Interface Library
1971  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1972  * YUI licensed under the BSD License:
1973  * http://developer.yahoo.net/yui/license.txt
1974  * <script type="text/javascript">
1975  *
1976  */
1977
1978 Roo.lib.Event = function() {
1979     var loadComplete = false;
1980     var listeners = [];
1981     var unloadListeners = [];
1982     var retryCount = 0;
1983     var onAvailStack = [];
1984     var counter = 0;
1985     var lastError = null;
1986
1987     return {
1988         POLL_RETRYS: 200,
1989         POLL_INTERVAL: 20,
1990         EL: 0,
1991         TYPE: 1,
1992         FN: 2,
1993         WFN: 3,
1994         OBJ: 3,
1995         ADJ_SCOPE: 4,
1996         _interval: null,
1997
1998         startInterval: function() {
1999             if (!this._interval) {
2000                 var self = this;
2001                 var callback = function() {
2002                     self._tryPreloadAttach();
2003                 };
2004                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2005
2006             }
2007         },
2008
2009         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2010             onAvailStack.push({ id:         p_id,
2011                 fn:         p_fn,
2012                 obj:        p_obj,
2013                 override:   p_override,
2014                 checkReady: false    });
2015
2016             retryCount = this.POLL_RETRYS;
2017             this.startInterval();
2018         },
2019
2020
2021         addListener: function(el, eventName, fn) {
2022             el = Roo.getDom(el);
2023             if (!el || !fn) {
2024                 return false;
2025             }
2026
2027             if ("unload" == eventName) {
2028                 unloadListeners[unloadListeners.length] =
2029                 [el, eventName, fn];
2030                 return true;
2031             }
2032
2033             var wrappedFn = function(e) {
2034                 return fn(Roo.lib.Event.getEvent(e));
2035             };
2036
2037             var li = [el, eventName, fn, wrappedFn];
2038
2039             var index = listeners.length;
2040             listeners[index] = li;
2041
2042             this.doAdd(el, eventName, wrappedFn, false);
2043             return true;
2044
2045         },
2046
2047
2048         removeListener: function(el, eventName, fn) {
2049             var i, len;
2050
2051             el = Roo.getDom(el);
2052
2053             if(!fn) {
2054                 return this.purgeElement(el, false, eventName);
2055             }
2056
2057
2058             if ("unload" == eventName) {
2059
2060                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2061                     var li = unloadListeners[i];
2062                     if (li &&
2063                         li[0] == el &&
2064                         li[1] == eventName &&
2065                         li[2] == fn) {
2066                         unloadListeners.splice(i, 1);
2067                         return true;
2068                     }
2069                 }
2070
2071                 return false;
2072             }
2073
2074             var cacheItem = null;
2075
2076
2077             var index = arguments[3];
2078
2079             if ("undefined" == typeof index) {
2080                 index = this._getCacheIndex(el, eventName, fn);
2081             }
2082
2083             if (index >= 0) {
2084                 cacheItem = listeners[index];
2085             }
2086
2087             if (!el || !cacheItem) {
2088                 return false;
2089             }
2090
2091             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2092
2093             delete listeners[index][this.WFN];
2094             delete listeners[index][this.FN];
2095             listeners.splice(index, 1);
2096
2097             return true;
2098
2099         },
2100
2101
2102         getTarget: function(ev, resolveTextNode) {
2103             ev = ev.browserEvent || ev;
2104             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2105             var t = ev.target || ev.srcElement;
2106             return this.resolveTextNode(t);
2107         },
2108
2109
2110         resolveTextNode: function(node) {
2111             if (Roo.isSafari && node && 3 == node.nodeType) {
2112                 return node.parentNode;
2113             } else {
2114                 return node;
2115             }
2116         },
2117
2118
2119         getPageX: function(ev) {
2120             ev = ev.browserEvent || ev;
2121             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2122             var x = ev.pageX;
2123             if (!x && 0 !== x) {
2124                 x = ev.clientX || 0;
2125
2126                 if (Roo.isIE) {
2127                     x += this.getScroll()[1];
2128                 }
2129             }
2130
2131             return x;
2132         },
2133
2134
2135         getPageY: function(ev) {
2136             ev = ev.browserEvent || ev;
2137             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2138             var y = ev.pageY;
2139             if (!y && 0 !== y) {
2140                 y = ev.clientY || 0;
2141
2142                 if (Roo.isIE) {
2143                     y += this.getScroll()[0];
2144                 }
2145             }
2146
2147
2148             return y;
2149         },
2150
2151
2152         getXY: function(ev) {
2153             ev = ev.browserEvent || ev;
2154             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2155             return [this.getPageX(ev), this.getPageY(ev)];
2156         },
2157
2158
2159         getRelatedTarget: function(ev) {
2160             ev = ev.browserEvent || ev;
2161             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2162             var t = ev.relatedTarget;
2163             if (!t) {
2164                 if (ev.type == "mouseout") {
2165                     t = ev.toElement;
2166                 } else if (ev.type == "mouseover") {
2167                     t = ev.fromElement;
2168                 }
2169             }
2170
2171             return this.resolveTextNode(t);
2172         },
2173
2174
2175         getTime: function(ev) {
2176             ev = ev.browserEvent || ev;
2177             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2178             if (!ev.time) {
2179                 var t = new Date().getTime();
2180                 try {
2181                     ev.time = t;
2182                 } catch(ex) {
2183                     this.lastError = ex;
2184                     return t;
2185                 }
2186             }
2187
2188             return ev.time;
2189         },
2190
2191
2192         stopEvent: function(ev) {
2193             this.stopPropagation(ev);
2194             this.preventDefault(ev);
2195         },
2196
2197
2198         stopPropagation: function(ev) {
2199             ev = ev.browserEvent || ev;
2200             if (ev.stopPropagation) {
2201                 ev.stopPropagation();
2202             } else {
2203                 ev.cancelBubble = true;
2204             }
2205         },
2206
2207
2208         preventDefault: function(ev) {
2209             ev = ev.browserEvent || ev;
2210             if(ev.preventDefault) {
2211                 ev.preventDefault();
2212             } else {
2213                 ev.returnValue = false;
2214             }
2215         },
2216
2217
2218         getEvent: function(e) {
2219             var ev = e || window.event;
2220             if (!ev) {
2221                 var c = this.getEvent.caller;
2222                 while (c) {
2223                     ev = c.arguments[0];
2224                     if (ev && Event == ev.constructor) {
2225                         break;
2226                     }
2227                     c = c.caller;
2228                 }
2229             }
2230             return ev;
2231         },
2232
2233
2234         getCharCode: function(ev) {
2235             ev = ev.browserEvent || ev;
2236             return ev.charCode || ev.keyCode || 0;
2237         },
2238
2239
2240         _getCacheIndex: function(el, eventName, fn) {
2241             for (var i = 0,len = listeners.length; i < len; ++i) {
2242                 var li = listeners[i];
2243                 if (li &&
2244                     li[this.FN] == fn &&
2245                     li[this.EL] == el &&
2246                     li[this.TYPE] == eventName) {
2247                     return i;
2248                 }
2249             }
2250
2251             return -1;
2252         },
2253
2254
2255         elCache: {},
2256
2257
2258         getEl: function(id) {
2259             return document.getElementById(id);
2260         },
2261
2262
2263         clearCache: function() {
2264         },
2265
2266
2267         _load: function(e) {
2268             loadComplete = true;
2269             var EU = Roo.lib.Event;
2270
2271
2272             if (Roo.isIE) {
2273                 EU.doRemove(window, "load", EU._load);
2274             }
2275         },
2276
2277
2278         _tryPreloadAttach: function() {
2279
2280             if (this.locked) {
2281                 return false;
2282             }
2283
2284             this.locked = true;
2285
2286
2287             var tryAgain = !loadComplete;
2288             if (!tryAgain) {
2289                 tryAgain = (retryCount > 0);
2290             }
2291
2292
2293             var notAvail = [];
2294             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2295                 var item = onAvailStack[i];
2296                 if (item) {
2297                     var el = this.getEl(item.id);
2298
2299                     if (el) {
2300                         if (!item.checkReady ||
2301                             loadComplete ||
2302                             el.nextSibling ||
2303                             (document && document.body)) {
2304
2305                             var scope = el;
2306                             if (item.override) {
2307                                 if (item.override === true) {
2308                                     scope = item.obj;
2309                                 } else {
2310                                     scope = item.override;
2311                                 }
2312                             }
2313                             item.fn.call(scope, item.obj);
2314                             onAvailStack[i] = null;
2315                         }
2316                     } else {
2317                         notAvail.push(item);
2318                     }
2319                 }
2320             }
2321
2322             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2323
2324             if (tryAgain) {
2325
2326                 this.startInterval();
2327             } else {
2328                 clearInterval(this._interval);
2329                 this._interval = null;
2330             }
2331
2332             this.locked = false;
2333
2334             return true;
2335
2336         },
2337
2338
2339         purgeElement: function(el, recurse, eventName) {
2340             var elListeners = this.getListeners(el, eventName);
2341             if (elListeners) {
2342                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2343                     var l = elListeners[i];
2344                     this.removeListener(el, l.type, l.fn);
2345                 }
2346             }
2347
2348             if (recurse && el && el.childNodes) {
2349                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2350                     this.purgeElement(el.childNodes[i], recurse, eventName);
2351                 }
2352             }
2353         },
2354
2355
2356         getListeners: function(el, eventName) {
2357             var results = [], searchLists;
2358             if (!eventName) {
2359                 searchLists = [listeners, unloadListeners];
2360             } else if (eventName == "unload") {
2361                 searchLists = [unloadListeners];
2362             } else {
2363                 searchLists = [listeners];
2364             }
2365
2366             for (var j = 0; j < searchLists.length; ++j) {
2367                 var searchList = searchLists[j];
2368                 if (searchList && searchList.length > 0) {
2369                     for (var i = 0,len = searchList.length; i < len; ++i) {
2370                         var l = searchList[i];
2371                         if (l && l[this.EL] === el &&
2372                             (!eventName || eventName === l[this.TYPE])) {
2373                             results.push({
2374                                 type:   l[this.TYPE],
2375                                 fn:     l[this.FN],
2376                                 obj:    l[this.OBJ],
2377                                 adjust: l[this.ADJ_SCOPE],
2378                                 index:  i
2379                             });
2380                         }
2381                     }
2382                 }
2383             }
2384
2385             return (results.length) ? results : null;
2386         },
2387
2388
2389         _unload: function(e) {
2390
2391             var EU = Roo.lib.Event, i, j, l, len, index;
2392
2393             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2394                 l = unloadListeners[i];
2395                 if (l) {
2396                     var scope = window;
2397                     if (l[EU.ADJ_SCOPE]) {
2398                         if (l[EU.ADJ_SCOPE] === true) {
2399                             scope = l[EU.OBJ];
2400                         } else {
2401                             scope = l[EU.ADJ_SCOPE];
2402                         }
2403                     }
2404                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2405                     unloadListeners[i] = null;
2406                     l = null;
2407                     scope = null;
2408                 }
2409             }
2410
2411             unloadListeners = null;
2412
2413             if (listeners && listeners.length > 0) {
2414                 j = listeners.length;
2415                 while (j) {
2416                     index = j - 1;
2417                     l = listeners[index];
2418                     if (l) {
2419                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2420                                 l[EU.FN], index);
2421                     }
2422                     j = j - 1;
2423                 }
2424                 l = null;
2425
2426                 EU.clearCache();
2427             }
2428
2429             EU.doRemove(window, "unload", EU._unload);
2430
2431         },
2432
2433
2434         getScroll: function() {
2435             var dd = document.documentElement, db = document.body;
2436             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2437                 return [dd.scrollTop, dd.scrollLeft];
2438             } else if (db) {
2439                 return [db.scrollTop, db.scrollLeft];
2440             } else {
2441                 return [0, 0];
2442             }
2443         },
2444
2445
2446         doAdd: function () {
2447             if (window.addEventListener) {
2448                 return function(el, eventName, fn, capture) {
2449                     el.addEventListener(eventName, fn, (capture));
2450                 };
2451             } else if (window.attachEvent) {
2452                 return function(el, eventName, fn, capture) {
2453                     el.attachEvent("on" + eventName, fn);
2454                 };
2455             } else {
2456                 return function() {
2457                 };
2458             }
2459         }(),
2460
2461
2462         doRemove: function() {
2463             if (window.removeEventListener) {
2464                 return function (el, eventName, fn, capture) {
2465                     el.removeEventListener(eventName, fn, (capture));
2466                 };
2467             } else if (window.detachEvent) {
2468                 return function (el, eventName, fn) {
2469                     el.detachEvent("on" + eventName, fn);
2470                 };
2471             } else {
2472                 return function() {
2473                 };
2474             }
2475         }()
2476     };
2477     
2478 }();
2479 (function() {     
2480    
2481     var E = Roo.lib.Event;
2482     E.on = E.addListener;
2483     E.un = E.removeListener;
2484
2485     if (document && document.body) {
2486         E._load();
2487     } else {
2488         E.doAdd(window, "load", E._load);
2489     }
2490     E.doAdd(window, "unload", E._unload);
2491     E._tryPreloadAttach();
2492 })();
2493
2494 /*
2495  * Portions of this file are based on pieces of Yahoo User Interface Library
2496  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2497  * YUI licensed under the BSD License:
2498  * http://developer.yahoo.net/yui/license.txt
2499  * <script type="text/javascript">
2500  *
2501  */
2502
2503 (function() {
2504     /**
2505      * @class Roo.lib.Ajax
2506      *
2507      */
2508     Roo.lib.Ajax = {
2509         /**
2510          * @static 
2511          */
2512         request : function(method, uri, cb, data, options) {
2513             if(options){
2514                 var hs = options.headers;
2515                 if(hs){
2516                     for(var h in hs){
2517                         if(hs.hasOwnProperty(h)){
2518                             this.initHeader(h, hs[h], false);
2519                         }
2520                     }
2521                 }
2522                 if(options.xmlData){
2523                     this.initHeader('Content-Type', 'text/xml', false);
2524                     method = 'POST';
2525                     data = options.xmlData;
2526                 }
2527             }
2528
2529             return this.asyncRequest(method, uri, cb, data);
2530         },
2531
2532         serializeForm : function(form) {
2533             if(typeof form == 'string') {
2534                 form = (document.getElementById(form) || document.forms[form]);
2535             }
2536
2537             var el, name, val, disabled, data = '', hasSubmit = false;
2538             for (var i = 0; i < form.elements.length; i++) {
2539                 el = form.elements[i];
2540                 disabled = form.elements[i].disabled;
2541                 name = form.elements[i].name;
2542                 val = form.elements[i].value;
2543
2544                 if (!disabled && name){
2545                     switch (el.type)
2546                             {
2547                         case 'select-one':
2548                         case 'select-multiple':
2549                             for (var j = 0; j < el.options.length; j++) {
2550                                 if (el.options[j].selected) {
2551                                     if (Roo.isIE) {
2552                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2553                                     }
2554                                     else {
2555                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2556                                     }
2557                                 }
2558                             }
2559                             break;
2560                         case 'radio':
2561                         case 'checkbox':
2562                             if (el.checked) {
2563                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2564                             }
2565                             break;
2566                         case 'file':
2567
2568                         case undefined:
2569
2570                         case 'reset':
2571
2572                         case 'button':
2573
2574                             break;
2575                         case 'submit':
2576                             if(hasSubmit == false) {
2577                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2578                                 hasSubmit = true;
2579                             }
2580                             break;
2581                         default:
2582                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2583                             break;
2584                     }
2585                 }
2586             }
2587             data = data.substr(0, data.length - 1);
2588             return data;
2589         },
2590
2591         headers:{},
2592
2593         hasHeaders:false,
2594
2595         useDefaultHeader:true,
2596
2597         defaultPostHeader:'application/x-www-form-urlencoded',
2598
2599         useDefaultXhrHeader:true,
2600
2601         defaultXhrHeader:'XMLHttpRequest',
2602
2603         hasDefaultHeaders:true,
2604
2605         defaultHeaders:{},
2606
2607         poll:{},
2608
2609         timeout:{},
2610
2611         pollInterval:50,
2612
2613         transactionId:0,
2614
2615         setProgId:function(id)
2616         {
2617             this.activeX.unshift(id);
2618         },
2619
2620         setDefaultPostHeader:function(b)
2621         {
2622             this.useDefaultHeader = b;
2623         },
2624
2625         setDefaultXhrHeader:function(b)
2626         {
2627             this.useDefaultXhrHeader = b;
2628         },
2629
2630         setPollingInterval:function(i)
2631         {
2632             if (typeof i == 'number' && isFinite(i)) {
2633                 this.pollInterval = i;
2634             }
2635         },
2636
2637         createXhrObject:function(transactionId)
2638         {
2639             var obj,http;
2640             try
2641             {
2642
2643                 http = new XMLHttpRequest();
2644
2645                 obj = { conn:http, tId:transactionId };
2646             }
2647             catch(e)
2648             {
2649                 for (var i = 0; i < this.activeX.length; ++i) {
2650                     try
2651                     {
2652
2653                         http = new ActiveXObject(this.activeX[i]);
2654
2655                         obj = { conn:http, tId:transactionId };
2656                         break;
2657                     }
2658                     catch(e) {
2659                     }
2660                 }
2661             }
2662             finally
2663             {
2664                 return obj;
2665             }
2666         },
2667
2668         getConnectionObject:function()
2669         {
2670             var o;
2671             var tId = this.transactionId;
2672
2673             try
2674             {
2675                 o = this.createXhrObject(tId);
2676                 if (o) {
2677                     this.transactionId++;
2678                 }
2679             }
2680             catch(e) {
2681             }
2682             finally
2683             {
2684                 return o;
2685             }
2686         },
2687
2688         asyncRequest:function(method, uri, callback, postData)
2689         {
2690             var o = this.getConnectionObject();
2691
2692             if (!o) {
2693                 return null;
2694             }
2695             else {
2696                 o.conn.open(method, uri, true);
2697
2698                 if (this.useDefaultXhrHeader) {
2699                     if (!this.defaultHeaders['X-Requested-With']) {
2700                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2701                     }
2702                 }
2703
2704                 if(postData && this.useDefaultHeader){
2705                     this.initHeader('Content-Type', this.defaultPostHeader);
2706                 }
2707
2708                  if (this.hasDefaultHeaders || this.hasHeaders) {
2709                     this.setHeader(o);
2710                 }
2711
2712                 this.handleReadyState(o, callback);
2713                 o.conn.send(postData || null);
2714
2715                 return o;
2716             }
2717         },
2718
2719         handleReadyState:function(o, callback)
2720         {
2721             var oConn = this;
2722
2723             if (callback && callback.timeout) {
2724                 
2725                 this.timeout[o.tId] = window.setTimeout(function() {
2726                     oConn.abort(o, callback, true);
2727                 }, callback.timeout);
2728             }
2729
2730             this.poll[o.tId] = window.setInterval(
2731                     function() {
2732                         if (o.conn && o.conn.readyState == 4) {
2733                             window.clearInterval(oConn.poll[o.tId]);
2734                             delete oConn.poll[o.tId];
2735
2736                             if(callback && callback.timeout) {
2737                                 window.clearTimeout(oConn.timeout[o.tId]);
2738                                 delete oConn.timeout[o.tId];
2739                             }
2740
2741                             oConn.handleTransactionResponse(o, callback);
2742                         }
2743                     }
2744                     , this.pollInterval);
2745         },
2746
2747         handleTransactionResponse:function(o, callback, isAbort)
2748         {
2749
2750             if (!callback) {
2751                 this.releaseObject(o);
2752                 return;
2753             }
2754
2755             var httpStatus, responseObject;
2756
2757             try
2758             {
2759                 if (o.conn.status !== undefined && o.conn.status != 0) {
2760                     httpStatus = o.conn.status;
2761                 }
2762                 else {
2763                     httpStatus = 13030;
2764                 }
2765             }
2766             catch(e) {
2767
2768
2769                 httpStatus = 13030;
2770             }
2771
2772             if (httpStatus >= 200 && httpStatus < 300) {
2773                 responseObject = this.createResponseObject(o, callback.argument);
2774                 if (callback.success) {
2775                     if (!callback.scope) {
2776                         callback.success(responseObject);
2777                     }
2778                     else {
2779
2780
2781                         callback.success.apply(callback.scope, [responseObject]);
2782                     }
2783                 }
2784             }
2785             else {
2786                 switch (httpStatus) {
2787
2788                     case 12002:
2789                     case 12029:
2790                     case 12030:
2791                     case 12031:
2792                     case 12152:
2793                     case 13030:
2794                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2795                         if (callback.failure) {
2796                             if (!callback.scope) {
2797                                 callback.failure(responseObject);
2798                             }
2799                             else {
2800                                 callback.failure.apply(callback.scope, [responseObject]);
2801                             }
2802                         }
2803                         break;
2804                     default:
2805                         responseObject = this.createResponseObject(o, callback.argument);
2806                         if (callback.failure) {
2807                             if (!callback.scope) {
2808                                 callback.failure(responseObject);
2809                             }
2810                             else {
2811                                 callback.failure.apply(callback.scope, [responseObject]);
2812                             }
2813                         }
2814                 }
2815             }
2816
2817             this.releaseObject(o);
2818             responseObject = null;
2819         },
2820
2821         createResponseObject:function(o, callbackArg)
2822         {
2823             var obj = {};
2824             var headerObj = {};
2825
2826             try
2827             {
2828                 var headerStr = o.conn.getAllResponseHeaders();
2829                 var header = headerStr.split('\n');
2830                 for (var i = 0; i < header.length; i++) {
2831                     var delimitPos = header[i].indexOf(':');
2832                     if (delimitPos != -1) {
2833                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2834                     }
2835                 }
2836             }
2837             catch(e) {
2838             }
2839
2840             obj.tId = o.tId;
2841             obj.status = o.conn.status;
2842             obj.statusText = o.conn.statusText;
2843             obj.getResponseHeader = headerObj;
2844             obj.getAllResponseHeaders = headerStr;
2845             obj.responseText = o.conn.responseText;
2846             obj.responseXML = o.conn.responseXML;
2847
2848             if (typeof callbackArg !== undefined) {
2849                 obj.argument = callbackArg;
2850             }
2851
2852             return obj;
2853         },
2854
2855         createExceptionObject:function(tId, callbackArg, isAbort)
2856         {
2857             var COMM_CODE = 0;
2858             var COMM_ERROR = 'communication failure';
2859             var ABORT_CODE = -1;
2860             var ABORT_ERROR = 'transaction aborted';
2861
2862             var obj = {};
2863
2864             obj.tId = tId;
2865             if (isAbort) {
2866                 obj.status = ABORT_CODE;
2867                 obj.statusText = ABORT_ERROR;
2868             }
2869             else {
2870                 obj.status = COMM_CODE;
2871                 obj.statusText = COMM_ERROR;
2872             }
2873
2874             if (callbackArg) {
2875                 obj.argument = callbackArg;
2876             }
2877
2878             return obj;
2879         },
2880
2881         initHeader:function(label, value, isDefault)
2882         {
2883             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2884
2885             if (headerObj[label] === undefined) {
2886                 headerObj[label] = value;
2887             }
2888             else {
2889
2890
2891                 headerObj[label] = value + "," + headerObj[label];
2892             }
2893
2894             if (isDefault) {
2895                 this.hasDefaultHeaders = true;
2896             }
2897             else {
2898                 this.hasHeaders = true;
2899             }
2900         },
2901
2902
2903         setHeader:function(o)
2904         {
2905             if (this.hasDefaultHeaders) {
2906                 for (var prop in this.defaultHeaders) {
2907                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2908                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2909                     }
2910                 }
2911             }
2912
2913             if (this.hasHeaders) {
2914                 for (var prop in this.headers) {
2915                     if (this.headers.hasOwnProperty(prop)) {
2916                         o.conn.setRequestHeader(prop, this.headers[prop]);
2917                     }
2918                 }
2919                 this.headers = {};
2920                 this.hasHeaders = false;
2921             }
2922         },
2923
2924         resetDefaultHeaders:function() {
2925             delete this.defaultHeaders;
2926             this.defaultHeaders = {};
2927             this.hasDefaultHeaders = false;
2928         },
2929
2930         abort:function(o, callback, isTimeout)
2931         {
2932             if(this.isCallInProgress(o)) {
2933                 o.conn.abort();
2934                 window.clearInterval(this.poll[o.tId]);
2935                 delete this.poll[o.tId];
2936                 if (isTimeout) {
2937                     delete this.timeout[o.tId];
2938                 }
2939
2940                 this.handleTransactionResponse(o, callback, true);
2941
2942                 return true;
2943             }
2944             else {
2945                 return false;
2946             }
2947         },
2948
2949
2950         isCallInProgress:function(o)
2951         {
2952             if (o && o.conn) {
2953                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2954             }
2955             else {
2956
2957                 return false;
2958             }
2959         },
2960
2961
2962         releaseObject:function(o)
2963         {
2964
2965             o.conn = null;
2966
2967             o = null;
2968         },
2969
2970         activeX:[
2971         'MSXML2.XMLHTTP.3.0',
2972         'MSXML2.XMLHTTP',
2973         'Microsoft.XMLHTTP'
2974         ]
2975
2976
2977     };
2978 })();/*
2979  * Portions of this file are based on pieces of Yahoo User Interface Library
2980  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2981  * YUI licensed under the BSD License:
2982  * http://developer.yahoo.net/yui/license.txt
2983  * <script type="text/javascript">
2984  *
2985  */
2986
2987 Roo.lib.Region = function(t, r, b, l) {
2988     this.top = t;
2989     this[1] = t;
2990     this.right = r;
2991     this.bottom = b;
2992     this.left = l;
2993     this[0] = l;
2994 };
2995
2996
2997 Roo.lib.Region.prototype = {
2998     contains : function(region) {
2999         return ( region.left >= this.left &&
3000                  region.right <= this.right &&
3001                  region.top >= this.top &&
3002                  region.bottom <= this.bottom    );
3003
3004     },
3005
3006     getArea : function() {
3007         return ( (this.bottom - this.top) * (this.right - this.left) );
3008     },
3009
3010     intersect : function(region) {
3011         var t = Math.max(this.top, region.top);
3012         var r = Math.min(this.right, region.right);
3013         var b = Math.min(this.bottom, region.bottom);
3014         var l = Math.max(this.left, region.left);
3015
3016         if (b >= t && r >= l) {
3017             return new Roo.lib.Region(t, r, b, l);
3018         } else {
3019             return null;
3020         }
3021     },
3022     union : function(region) {
3023         var t = Math.min(this.top, region.top);
3024         var r = Math.max(this.right, region.right);
3025         var b = Math.max(this.bottom, region.bottom);
3026         var l = Math.min(this.left, region.left);
3027
3028         return new Roo.lib.Region(t, r, b, l);
3029     },
3030
3031     adjust : function(t, l, b, r) {
3032         this.top += t;
3033         this.left += l;
3034         this.right += r;
3035         this.bottom += b;
3036         return this;
3037     }
3038 };
3039
3040 Roo.lib.Region.getRegion = function(el) {
3041     var p = Roo.lib.Dom.getXY(el);
3042
3043     var t = p[1];
3044     var r = p[0] + el.offsetWidth;
3045     var b = p[1] + el.offsetHeight;
3046     var l = p[0];
3047
3048     return new Roo.lib.Region(t, r, b, l);
3049 };
3050 /*
3051  * Portions of this file are based on pieces of Yahoo User Interface Library
3052  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3053  * YUI licensed under the BSD License:
3054  * http://developer.yahoo.net/yui/license.txt
3055  * <script type="text/javascript">
3056  *
3057  */
3058 //@@dep Roo.lib.Region
3059
3060
3061 Roo.lib.Point = function(x, y) {
3062     if (x instanceof Array) {
3063         y = x[1];
3064         x = x[0];
3065     }
3066     this.x = this.right = this.left = this[0] = x;
3067     this.y = this.top = this.bottom = this[1] = y;
3068 };
3069
3070 Roo.lib.Point.prototype = new Roo.lib.Region();
3071 /*
3072  * Portions of this file are based on pieces of Yahoo User Interface Library
3073  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3074  * YUI licensed under the BSD License:
3075  * http://developer.yahoo.net/yui/license.txt
3076  * <script type="text/javascript">
3077  *
3078  */
3079  
3080 (function() {   
3081
3082     Roo.lib.Anim = {
3083         scroll : function(el, args, duration, easing, cb, scope) {
3084             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3085         },
3086
3087         motion : function(el, args, duration, easing, cb, scope) {
3088             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3089         },
3090
3091         color : function(el, args, duration, easing, cb, scope) {
3092             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3093         },
3094
3095         run : function(el, args, duration, easing, cb, scope, type) {
3096             type = type || Roo.lib.AnimBase;
3097             if (typeof easing == "string") {
3098                 easing = Roo.lib.Easing[easing];
3099             }
3100             var anim = new type(el, args, duration, easing);
3101             anim.animateX(function() {
3102                 Roo.callback(cb, scope);
3103             });
3104             return anim;
3105         }
3106     };
3107 })();/*
3108  * Portions of this file are based on pieces of Yahoo User Interface Library
3109  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3110  * YUI licensed under the BSD License:
3111  * http://developer.yahoo.net/yui/license.txt
3112  * <script type="text/javascript">
3113  *
3114  */
3115
3116 (function() {    
3117     var libFlyweight;
3118     
3119     function fly(el) {
3120         if (!libFlyweight) {
3121             libFlyweight = new Roo.Element.Flyweight();
3122         }
3123         libFlyweight.dom = el;
3124         return libFlyweight;
3125     }
3126
3127     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3128     
3129    
3130     
3131     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3132         if (el) {
3133             this.init(el, attributes, duration, method);
3134         }
3135     };
3136
3137     Roo.lib.AnimBase.fly = fly;
3138     
3139     
3140     
3141     Roo.lib.AnimBase.prototype = {
3142
3143         toString: function() {
3144             var el = this.getEl();
3145             var id = el.id || el.tagName;
3146             return ("Anim " + id);
3147         },
3148
3149         patterns: {
3150             noNegatives:        /width|height|opacity|padding/i,
3151             offsetAttribute:  /^((width|height)|(top|left))$/,
3152             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3153             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3154         },
3155
3156
3157         doMethod: function(attr, start, end) {
3158             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3159         },
3160
3161
3162         setAttribute: function(attr, val, unit) {
3163             if (this.patterns.noNegatives.test(attr)) {
3164                 val = (val > 0) ? val : 0;
3165             }
3166
3167             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3168         },
3169
3170
3171         getAttribute: function(attr) {
3172             var el = this.getEl();
3173             var val = fly(el).getStyle(attr);
3174
3175             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3176                 return parseFloat(val);
3177             }
3178
3179             var a = this.patterns.offsetAttribute.exec(attr) || [];
3180             var pos = !!( a[3] );
3181             var box = !!( a[2] );
3182
3183
3184             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3185                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3186             } else {
3187                 val = 0;
3188             }
3189
3190             return val;
3191         },
3192
3193
3194         getDefaultUnit: function(attr) {
3195             if (this.patterns.defaultUnit.test(attr)) {
3196                 return 'px';
3197             }
3198
3199             return '';
3200         },
3201
3202         animateX : function(callback, scope) {
3203             var f = function() {
3204                 this.onComplete.removeListener(f);
3205                 if (typeof callback == "function") {
3206                     callback.call(scope || this, this);
3207                 }
3208             };
3209             this.onComplete.addListener(f, this);
3210             this.animate();
3211         },
3212
3213
3214         setRuntimeAttribute: function(attr) {
3215             var start;
3216             var end;
3217             var attributes = this.attributes;
3218
3219             this.runtimeAttributes[attr] = {};
3220
3221             var isset = function(prop) {
3222                 return (typeof prop !== 'undefined');
3223             };
3224
3225             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3226                 return false;
3227             }
3228
3229             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3230
3231
3232             if (isset(attributes[attr]['to'])) {
3233                 end = attributes[attr]['to'];
3234             } else if (isset(attributes[attr]['by'])) {
3235                 if (start.constructor == Array) {
3236                     end = [];
3237                     for (var i = 0, len = start.length; i < len; ++i) {
3238                         end[i] = start[i] + attributes[attr]['by'][i];
3239                     }
3240                 } else {
3241                     end = start + attributes[attr]['by'];
3242                 }
3243             }
3244
3245             this.runtimeAttributes[attr].start = start;
3246             this.runtimeAttributes[attr].end = end;
3247
3248
3249             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3250         },
3251
3252
3253         init: function(el, attributes, duration, method) {
3254
3255             var isAnimated = false;
3256
3257
3258             var startTime = null;
3259
3260
3261             var actualFrames = 0;
3262
3263
3264             el = Roo.getDom(el);
3265
3266
3267             this.attributes = attributes || {};
3268
3269
3270             this.duration = duration || 1;
3271
3272
3273             this.method = method || Roo.lib.Easing.easeNone;
3274
3275
3276             this.useSeconds = true;
3277
3278
3279             this.currentFrame = 0;
3280
3281
3282             this.totalFrames = Roo.lib.AnimMgr.fps;
3283
3284
3285             this.getEl = function() {
3286                 return el;
3287             };
3288
3289
3290             this.isAnimated = function() {
3291                 return isAnimated;
3292             };
3293
3294
3295             this.getStartTime = function() {
3296                 return startTime;
3297             };
3298
3299             this.runtimeAttributes = {};
3300
3301
3302             this.animate = function() {
3303                 if (this.isAnimated()) {
3304                     return false;
3305                 }
3306
3307                 this.currentFrame = 0;
3308
3309                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3310
3311                 Roo.lib.AnimMgr.registerElement(this);
3312             };
3313
3314
3315             this.stop = function(finish) {
3316                 if (finish) {
3317                     this.currentFrame = this.totalFrames;
3318                     this._onTween.fire();
3319                 }
3320                 Roo.lib.AnimMgr.stop(this);
3321             };
3322
3323             var onStart = function() {
3324                 this.onStart.fire();
3325
3326                 this.runtimeAttributes = {};
3327                 for (var attr in this.attributes) {
3328                     this.setRuntimeAttribute(attr);
3329                 }
3330
3331                 isAnimated = true;
3332                 actualFrames = 0;
3333                 startTime = new Date();
3334             };
3335
3336
3337             var onTween = function() {
3338                 var data = {
3339                     duration: new Date() - this.getStartTime(),
3340                     currentFrame: this.currentFrame
3341                 };
3342
3343                 data.toString = function() {
3344                     return (
3345                             'duration: ' + data.duration +
3346                             ', currentFrame: ' + data.currentFrame
3347                             );
3348                 };
3349
3350                 this.onTween.fire(data);
3351
3352                 var runtimeAttributes = this.runtimeAttributes;
3353
3354                 for (var attr in runtimeAttributes) {
3355                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3356                 }
3357
3358                 actualFrames += 1;
3359             };
3360
3361             var onComplete = function() {
3362                 var actual_duration = (new Date() - startTime) / 1000 ;
3363
3364                 var data = {
3365                     duration: actual_duration,
3366                     frames: actualFrames,
3367                     fps: actualFrames / actual_duration
3368                 };
3369
3370                 data.toString = function() {
3371                     return (
3372                             'duration: ' + data.duration +
3373                             ', frames: ' + data.frames +
3374                             ', fps: ' + data.fps
3375                             );
3376                 };
3377
3378                 isAnimated = false;
3379                 actualFrames = 0;
3380                 this.onComplete.fire(data);
3381             };
3382
3383
3384             this._onStart = new Roo.util.Event(this);
3385             this.onStart = new Roo.util.Event(this);
3386             this.onTween = new Roo.util.Event(this);
3387             this._onTween = new Roo.util.Event(this);
3388             this.onComplete = new Roo.util.Event(this);
3389             this._onComplete = new Roo.util.Event(this);
3390             this._onStart.addListener(onStart);
3391             this._onTween.addListener(onTween);
3392             this._onComplete.addListener(onComplete);
3393         }
3394     };
3395 })();
3396 /*
3397  * Portions of this file are based on pieces of Yahoo User Interface Library
3398  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3399  * YUI licensed under the BSD License:
3400  * http://developer.yahoo.net/yui/license.txt
3401  * <script type="text/javascript">
3402  *
3403  */
3404
3405 Roo.lib.AnimMgr = new function() {
3406
3407     var thread = null;
3408
3409
3410     var queue = [];
3411
3412
3413     var tweenCount = 0;
3414
3415
3416     this.fps = 1000;
3417
3418
3419     this.delay = 1;
3420
3421
3422     this.registerElement = function(tween) {
3423         queue[queue.length] = tween;
3424         tweenCount += 1;
3425         tween._onStart.fire();
3426         this.start();
3427     };
3428
3429
3430     this.unRegister = function(tween, index) {
3431         tween._onComplete.fire();
3432         index = index || getIndex(tween);
3433         if (index != -1) {
3434             queue.splice(index, 1);
3435         }
3436
3437         tweenCount -= 1;
3438         if (tweenCount <= 0) {
3439             this.stop();
3440         }
3441     };
3442
3443
3444     this.start = function() {
3445         if (thread === null) {
3446             thread = setInterval(this.run, this.delay);
3447         }
3448     };
3449
3450
3451     this.stop = function(tween) {
3452         if (!tween) {
3453             clearInterval(thread);
3454
3455             for (var i = 0, len = queue.length; i < len; ++i) {
3456                 if (queue[0].isAnimated()) {
3457                     this.unRegister(queue[0], 0);
3458                 }
3459             }
3460
3461             queue = [];
3462             thread = null;
3463             tweenCount = 0;
3464         }
3465         else {
3466             this.unRegister(tween);
3467         }
3468     };
3469
3470
3471     this.run = function() {
3472         for (var i = 0, len = queue.length; i < len; ++i) {
3473             var tween = queue[i];
3474             if (!tween || !tween.isAnimated()) {
3475                 continue;
3476             }
3477
3478             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3479             {
3480                 tween.currentFrame += 1;
3481
3482                 if (tween.useSeconds) {
3483                     correctFrame(tween);
3484                 }
3485                 tween._onTween.fire();
3486             }
3487             else {
3488                 Roo.lib.AnimMgr.stop(tween, i);
3489             }
3490         }
3491     };
3492
3493     var getIndex = function(anim) {
3494         for (var i = 0, len = queue.length; i < len; ++i) {
3495             if (queue[i] == anim) {
3496                 return i;
3497             }
3498         }
3499         return -1;
3500     };
3501
3502
3503     var correctFrame = function(tween) {
3504         var frames = tween.totalFrames;
3505         var frame = tween.currentFrame;
3506         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3507         var elapsed = (new Date() - tween.getStartTime());
3508         var tweak = 0;
3509
3510         if (elapsed < tween.duration * 1000) {
3511             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3512         } else {
3513             tweak = frames - (frame + 1);
3514         }
3515         if (tweak > 0 && isFinite(tweak)) {
3516             if (tween.currentFrame + tweak >= frames) {
3517                 tweak = frames - (frame + 1);
3518             }
3519
3520             tween.currentFrame += tweak;
3521         }
3522     };
3523 };
3524
3525     /*
3526  * Portions of this file are based on pieces of Yahoo User Interface Library
3527  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3528  * YUI licensed under the BSD License:
3529  * http://developer.yahoo.net/yui/license.txt
3530  * <script type="text/javascript">
3531  *
3532  */
3533 Roo.lib.Bezier = new function() {
3534
3535         this.getPosition = function(points, t) {
3536             var n = points.length;
3537             var tmp = [];
3538
3539             for (var i = 0; i < n; ++i) {
3540                 tmp[i] = [points[i][0], points[i][1]];
3541             }
3542
3543             for (var j = 1; j < n; ++j) {
3544                 for (i = 0; i < n - j; ++i) {
3545                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3546                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3547                 }
3548             }
3549
3550             return [ tmp[0][0], tmp[0][1] ];
3551
3552         };
3553     };/*
3554  * Portions of this file are based on pieces of Yahoo User Interface Library
3555  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3556  * YUI licensed under the BSD License:
3557  * http://developer.yahoo.net/yui/license.txt
3558  * <script type="text/javascript">
3559  *
3560  */
3561 (function() {
3562
3563     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3564         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3565     };
3566
3567     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3568
3569     var fly = Roo.lib.AnimBase.fly;
3570     var Y = Roo.lib;
3571     var superclass = Y.ColorAnim.superclass;
3572     var proto = Y.ColorAnim.prototype;
3573
3574     proto.toString = function() {
3575         var el = this.getEl();
3576         var id = el.id || el.tagName;
3577         return ("ColorAnim " + id);
3578     };
3579
3580     proto.patterns.color = /color$/i;
3581     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3582     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3583     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3584     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3585
3586
3587     proto.parseColor = function(s) {
3588         if (s.length == 3) {
3589             return s;
3590         }
3591
3592         var c = this.patterns.hex.exec(s);
3593         if (c && c.length == 4) {
3594             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3595         }
3596
3597         c = this.patterns.rgb.exec(s);
3598         if (c && c.length == 4) {
3599             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3600         }
3601
3602         c = this.patterns.hex3.exec(s);
3603         if (c && c.length == 4) {
3604             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3605         }
3606
3607         return null;
3608     };
3609     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3610     proto.getAttribute = function(attr) {
3611         var el = this.getEl();
3612         if (this.patterns.color.test(attr)) {
3613             var val = fly(el).getStyle(attr);
3614
3615             if (this.patterns.transparent.test(val)) {
3616                 var parent = el.parentNode;
3617                 val = fly(parent).getStyle(attr);
3618
3619                 while (parent && this.patterns.transparent.test(val)) {
3620                     parent = parent.parentNode;
3621                     val = fly(parent).getStyle(attr);
3622                     if (parent.tagName.toUpperCase() == 'HTML') {
3623                         val = '#fff';
3624                     }
3625                 }
3626             }
3627         } else {
3628             val = superclass.getAttribute.call(this, attr);
3629         }
3630
3631         return val;
3632     };
3633     proto.getAttribute = function(attr) {
3634         var el = this.getEl();
3635         if (this.patterns.color.test(attr)) {
3636             var val = fly(el).getStyle(attr);
3637
3638             if (this.patterns.transparent.test(val)) {
3639                 var parent = el.parentNode;
3640                 val = fly(parent).getStyle(attr);
3641
3642                 while (parent && this.patterns.transparent.test(val)) {
3643                     parent = parent.parentNode;
3644                     val = fly(parent).getStyle(attr);
3645                     if (parent.tagName.toUpperCase() == 'HTML') {
3646                         val = '#fff';
3647                     }
3648                 }
3649             }
3650         } else {
3651             val = superclass.getAttribute.call(this, attr);
3652         }
3653
3654         return val;
3655     };
3656
3657     proto.doMethod = function(attr, start, end) {
3658         var val;
3659
3660         if (this.patterns.color.test(attr)) {
3661             val = [];
3662             for (var i = 0, len = start.length; i < len; ++i) {
3663                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3664             }
3665
3666             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3667         }
3668         else {
3669             val = superclass.doMethod.call(this, attr, start, end);
3670         }
3671
3672         return val;
3673     };
3674
3675     proto.setRuntimeAttribute = function(attr) {
3676         superclass.setRuntimeAttribute.call(this, attr);
3677
3678         if (this.patterns.color.test(attr)) {
3679             var attributes = this.attributes;
3680             var start = this.parseColor(this.runtimeAttributes[attr].start);
3681             var end = this.parseColor(this.runtimeAttributes[attr].end);
3682
3683             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3684                 end = this.parseColor(attributes[attr].by);
3685
3686                 for (var i = 0, len = start.length; i < len; ++i) {
3687                     end[i] = start[i] + end[i];
3688                 }
3689             }
3690
3691             this.runtimeAttributes[attr].start = start;
3692             this.runtimeAttributes[attr].end = end;
3693         }
3694     };
3695 })();
3696
3697 /*
3698  * Portions of this file are based on pieces of Yahoo User Interface Library
3699  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3700  * YUI licensed under the BSD License:
3701  * http://developer.yahoo.net/yui/license.txt
3702  * <script type="text/javascript">
3703  *
3704  */
3705 Roo.lib.Easing = {
3706
3707
3708     easeNone: function (t, b, c, d) {
3709         return c * t / d + b;
3710     },
3711
3712
3713     easeIn: function (t, b, c, d) {
3714         return c * (t /= d) * t + b;
3715     },
3716
3717
3718     easeOut: function (t, b, c, d) {
3719         return -c * (t /= d) * (t - 2) + b;
3720     },
3721
3722
3723     easeBoth: function (t, b, c, d) {
3724         if ((t /= d / 2) < 1) {
3725             return c / 2 * t * t + b;
3726         }
3727
3728         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3729     },
3730
3731
3732     easeInStrong: function (t, b, c, d) {
3733         return c * (t /= d) * t * t * t + b;
3734     },
3735
3736
3737     easeOutStrong: function (t, b, c, d) {
3738         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3739     },
3740
3741
3742     easeBothStrong: function (t, b, c, d) {
3743         if ((t /= d / 2) < 1) {
3744             return c / 2 * t * t * t * t + b;
3745         }
3746
3747         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3748     },
3749
3750
3751
3752     elasticIn: function (t, b, c, d, a, p) {
3753         if (t == 0) {
3754             return b;
3755         }
3756         if ((t /= d) == 1) {
3757             return b + c;
3758         }
3759         if (!p) {
3760             p = d * .3;
3761         }
3762
3763         if (!a || a < Math.abs(c)) {
3764             a = c;
3765             var s = p / 4;
3766         }
3767         else {
3768             var s = p / (2 * Math.PI) * Math.asin(c / a);
3769         }
3770
3771         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3772     },
3773
3774
3775     elasticOut: function (t, b, c, d, a, p) {
3776         if (t == 0) {
3777             return b;
3778         }
3779         if ((t /= d) == 1) {
3780             return b + c;
3781         }
3782         if (!p) {
3783             p = d * .3;
3784         }
3785
3786         if (!a || a < Math.abs(c)) {
3787             a = c;
3788             var s = p / 4;
3789         }
3790         else {
3791             var s = p / (2 * Math.PI) * Math.asin(c / a);
3792         }
3793
3794         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3795     },
3796
3797
3798     elasticBoth: function (t, b, c, d, a, p) {
3799         if (t == 0) {
3800             return b;
3801         }
3802
3803         if ((t /= d / 2) == 2) {
3804             return b + c;
3805         }
3806
3807         if (!p) {
3808             p = d * (.3 * 1.5);
3809         }
3810
3811         if (!a || a < Math.abs(c)) {
3812             a = c;
3813             var s = p / 4;
3814         }
3815         else {
3816             var s = p / (2 * Math.PI) * Math.asin(c / a);
3817         }
3818
3819         if (t < 1) {
3820             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3821                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3822         }
3823         return a * Math.pow(2, -10 * (t -= 1)) *
3824                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3825     },
3826
3827
3828
3829     backIn: function (t, b, c, d, s) {
3830         if (typeof s == 'undefined') {
3831             s = 1.70158;
3832         }
3833         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3834     },
3835
3836
3837     backOut: function (t, b, c, d, s) {
3838         if (typeof s == 'undefined') {
3839             s = 1.70158;
3840         }
3841         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3842     },
3843
3844
3845     backBoth: function (t, b, c, d, s) {
3846         if (typeof s == 'undefined') {
3847             s = 1.70158;
3848         }
3849
3850         if ((t /= d / 2 ) < 1) {
3851             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3852         }
3853         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3854     },
3855
3856
3857     bounceIn: function (t, b, c, d) {
3858         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3859     },
3860
3861
3862     bounceOut: function (t, b, c, d) {
3863         if ((t /= d) < (1 / 2.75)) {
3864             return c * (7.5625 * t * t) + b;
3865         } else if (t < (2 / 2.75)) {
3866             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3867         } else if (t < (2.5 / 2.75)) {
3868             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3869         }
3870         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3871     },
3872
3873
3874     bounceBoth: function (t, b, c, d) {
3875         if (t < d / 2) {
3876             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3877         }
3878         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3879     }
3880 };/*
3881  * Portions of this file are based on pieces of Yahoo User Interface Library
3882  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3883  * YUI licensed under the BSD License:
3884  * http://developer.yahoo.net/yui/license.txt
3885  * <script type="text/javascript">
3886  *
3887  */
3888     (function() {
3889         Roo.lib.Motion = function(el, attributes, duration, method) {
3890             if (el) {
3891                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3892             }
3893         };
3894
3895         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3896
3897
3898         var Y = Roo.lib;
3899         var superclass = Y.Motion.superclass;
3900         var proto = Y.Motion.prototype;
3901
3902         proto.toString = function() {
3903             var el = this.getEl();
3904             var id = el.id || el.tagName;
3905             return ("Motion " + id);
3906         };
3907
3908         proto.patterns.points = /^points$/i;
3909
3910         proto.setAttribute = function(attr, val, unit) {
3911             if (this.patterns.points.test(attr)) {
3912                 unit = unit || 'px';
3913                 superclass.setAttribute.call(this, 'left', val[0], unit);
3914                 superclass.setAttribute.call(this, 'top', val[1], unit);
3915             } else {
3916                 superclass.setAttribute.call(this, attr, val, unit);
3917             }
3918         };
3919
3920         proto.getAttribute = function(attr) {
3921             if (this.patterns.points.test(attr)) {
3922                 var val = [
3923                         superclass.getAttribute.call(this, 'left'),
3924                         superclass.getAttribute.call(this, 'top')
3925                         ];
3926             } else {
3927                 val = superclass.getAttribute.call(this, attr);
3928             }
3929
3930             return val;
3931         };
3932
3933         proto.doMethod = function(attr, start, end) {
3934             var val = null;
3935
3936             if (this.patterns.points.test(attr)) {
3937                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3938                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3939             } else {
3940                 val = superclass.doMethod.call(this, attr, start, end);
3941             }
3942             return val;
3943         };
3944
3945         proto.setRuntimeAttribute = function(attr) {
3946             if (this.patterns.points.test(attr)) {
3947                 var el = this.getEl();
3948                 var attributes = this.attributes;
3949                 var start;
3950                 var control = attributes['points']['control'] || [];
3951                 var end;
3952                 var i, len;
3953
3954                 if (control.length > 0 && !(control[0] instanceof Array)) {
3955                     control = [control];
3956                 } else {
3957                     var tmp = [];
3958                     for (i = 0,len = control.length; i < len; ++i) {
3959                         tmp[i] = control[i];
3960                     }
3961                     control = tmp;
3962                 }
3963
3964                 Roo.fly(el).position();
3965
3966                 if (isset(attributes['points']['from'])) {
3967                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3968                 }
3969                 else {
3970                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3971                 }
3972
3973                 start = this.getAttribute('points');
3974
3975
3976                 if (isset(attributes['points']['to'])) {
3977                     end = translateValues.call(this, attributes['points']['to'], start);
3978
3979                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3980                     for (i = 0,len = control.length; i < len; ++i) {
3981                         control[i] = translateValues.call(this, control[i], start);
3982                     }
3983
3984
3985                 } else if (isset(attributes['points']['by'])) {
3986                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3987
3988                     for (i = 0,len = control.length; i < len; ++i) {
3989                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3990                     }
3991                 }
3992
3993                 this.runtimeAttributes[attr] = [start];
3994
3995                 if (control.length > 0) {
3996                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3997                 }
3998
3999                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4000             }
4001             else {
4002                 superclass.setRuntimeAttribute.call(this, attr);
4003             }
4004         };
4005
4006         var translateValues = function(val, start) {
4007             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4008             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4009
4010             return val;
4011         };
4012
4013         var isset = function(prop) {
4014             return (typeof prop !== 'undefined');
4015         };
4016     })();
4017 /*
4018  * Portions of this file are based on pieces of Yahoo User Interface Library
4019  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4020  * YUI licensed under the BSD License:
4021  * http://developer.yahoo.net/yui/license.txt
4022  * <script type="text/javascript">
4023  *
4024  */
4025     (function() {
4026         Roo.lib.Scroll = function(el, attributes, duration, method) {
4027             if (el) {
4028                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4029             }
4030         };
4031
4032         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4033
4034
4035         var Y = Roo.lib;
4036         var superclass = Y.Scroll.superclass;
4037         var proto = Y.Scroll.prototype;
4038
4039         proto.toString = function() {
4040             var el = this.getEl();
4041             var id = el.id || el.tagName;
4042             return ("Scroll " + id);
4043         };
4044
4045         proto.doMethod = function(attr, start, end) {
4046             var val = null;
4047
4048             if (attr == 'scroll') {
4049                 val = [
4050                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4051                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4052                         ];
4053
4054             } else {
4055                 val = superclass.doMethod.call(this, attr, start, end);
4056             }
4057             return val;
4058         };
4059
4060         proto.getAttribute = function(attr) {
4061             var val = null;
4062             var el = this.getEl();
4063
4064             if (attr == 'scroll') {
4065                 val = [ el.scrollLeft, el.scrollTop ];
4066             } else {
4067                 val = superclass.getAttribute.call(this, attr);
4068             }
4069
4070             return val;
4071         };
4072
4073         proto.setAttribute = function(attr, val, unit) {
4074             var el = this.getEl();
4075
4076             if (attr == 'scroll') {
4077                 el.scrollLeft = val[0];
4078                 el.scrollTop = val[1];
4079             } else {
4080                 superclass.setAttribute.call(this, attr, val, unit);
4081             }
4082         };
4083     })();
4084 /*
4085  * Based on:
4086  * Ext JS Library 1.1.1
4087  * Copyright(c) 2006-2007, Ext JS, LLC.
4088  *
4089  * Originally Released Under LGPL - original licence link has changed is not relivant.
4090  *
4091  * Fork - LGPL
4092  * <script type="text/javascript">
4093  */
4094
4095
4096 // nasty IE9 hack - what a pile of crap that is..
4097
4098  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4099     Range.prototype.createContextualFragment = function (html) {
4100         var doc = window.document;
4101         var container = doc.createElement("div");
4102         container.innerHTML = html;
4103         var frag = doc.createDocumentFragment(), n;
4104         while ((n = container.firstChild)) {
4105             frag.appendChild(n);
4106         }
4107         return frag;
4108     };
4109 }
4110
4111 /**
4112  * @class Roo.DomHelper
4113  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4114  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4115  * @singleton
4116  */
4117 Roo.DomHelper = function(){
4118     var tempTableEl = null;
4119     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4120     var tableRe = /^table|tbody|tr|td$/i;
4121     var xmlns = {};
4122     // build as innerHTML where available
4123     /** @ignore */
4124     var createHtml = function(o){
4125         if(typeof o == 'string'){
4126             return o;
4127         }
4128         var b = "";
4129         if(!o.tag){
4130             o.tag = "div";
4131         }
4132         b += "<" + o.tag;
4133         for(var attr in o){
4134             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4135             if(attr == "style"){
4136                 var s = o["style"];
4137                 if(typeof s == "function"){
4138                     s = s.call();
4139                 }
4140                 if(typeof s == "string"){
4141                     b += ' style="' + s + '"';
4142                 }else if(typeof s == "object"){
4143                     b += ' style="';
4144                     for(var key in s){
4145                         if(typeof s[key] != "function"){
4146                             b += key + ":" + s[key] + ";";
4147                         }
4148                     }
4149                     b += '"';
4150                 }
4151             }else{
4152                 if(attr == "cls"){
4153                     b += ' class="' + o["cls"] + '"';
4154                 }else if(attr == "htmlFor"){
4155                     b += ' for="' + o["htmlFor"] + '"';
4156                 }else{
4157                     b += " " + attr + '="' + o[attr] + '"';
4158                 }
4159             }
4160         }
4161         if(emptyTags.test(o.tag)){
4162             b += "/>";
4163         }else{
4164             b += ">";
4165             var cn = o.children || o.cn;
4166             if(cn){
4167                 //http://bugs.kde.org/show_bug.cgi?id=71506
4168                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4169                     for(var i = 0, len = cn.length; i < len; i++) {
4170                         b += createHtml(cn[i], b);
4171                     }
4172                 }else{
4173                     b += createHtml(cn, b);
4174                 }
4175             }
4176             if(o.html){
4177                 b += o.html;
4178             }
4179             b += "</" + o.tag + ">";
4180         }
4181         return b;
4182     };
4183
4184     // build as dom
4185     /** @ignore */
4186     var createDom = function(o, parentNode){
4187          
4188         // defininition craeted..
4189         var ns = false;
4190         if (o.ns && o.ns != 'html') {
4191                
4192             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4193                 xmlns[o.ns] = o.xmlns;
4194                 ns = o.xmlns;
4195             }
4196             if (typeof(xmlns[o.ns]) == 'undefined') {
4197                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4198             }
4199             ns = xmlns[o.ns];
4200         }
4201         
4202         
4203         if (typeof(o) == 'string') {
4204             return parentNode.appendChild(document.createTextNode(o));
4205         }
4206         o.tag = o.tag || div;
4207         if (o.ns && Roo.isIE) {
4208             ns = false;
4209             o.tag = o.ns + ':' + o.tag;
4210             
4211         }
4212         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4213         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4214         for(var attr in o){
4215             
4216             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4217                     attr == "style" || typeof o[attr] == "function") continue;
4218                     
4219             if(attr=="cls" && Roo.isIE){
4220                 el.className = o["cls"];
4221             }else{
4222                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4223                 else el[attr] = o[attr];
4224             }
4225         }
4226         Roo.DomHelper.applyStyles(el, o.style);
4227         var cn = o.children || o.cn;
4228         if(cn){
4229             //http://bugs.kde.org/show_bug.cgi?id=71506
4230              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4231                 for(var i = 0, len = cn.length; i < len; i++) {
4232                     createDom(cn[i], el);
4233                 }
4234             }else{
4235                 createDom(cn, el);
4236             }
4237         }
4238         if(o.html){
4239             el.innerHTML = o.html;
4240         }
4241         if(parentNode){
4242            parentNode.appendChild(el);
4243         }
4244         return el;
4245     };
4246
4247     var ieTable = function(depth, s, h, e){
4248         tempTableEl.innerHTML = [s, h, e].join('');
4249         var i = -1, el = tempTableEl;
4250         while(++i < depth){
4251             el = el.firstChild;
4252         }
4253         return el;
4254     };
4255
4256     // kill repeat to save bytes
4257     var ts = '<table>',
4258         te = '</table>',
4259         tbs = ts+'<tbody>',
4260         tbe = '</tbody>'+te,
4261         trs = tbs + '<tr>',
4262         tre = '</tr>'+tbe;
4263
4264     /**
4265      * @ignore
4266      * Nasty code for IE's broken table implementation
4267      */
4268     var insertIntoTable = function(tag, where, el, html){
4269         if(!tempTableEl){
4270             tempTableEl = document.createElement('div');
4271         }
4272         var node;
4273         var before = null;
4274         if(tag == 'td'){
4275             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4276                 return;
4277             }
4278             if(where == 'beforebegin'){
4279                 before = el;
4280                 el = el.parentNode;
4281             } else{
4282                 before = el.nextSibling;
4283                 el = el.parentNode;
4284             }
4285             node = ieTable(4, trs, html, tre);
4286         }
4287         else if(tag == 'tr'){
4288             if(where == 'beforebegin'){
4289                 before = el;
4290                 el = el.parentNode;
4291                 node = ieTable(3, tbs, html, tbe);
4292             } else if(where == 'afterend'){
4293                 before = el.nextSibling;
4294                 el = el.parentNode;
4295                 node = ieTable(3, tbs, html, tbe);
4296             } else{ // INTO a TR
4297                 if(where == 'afterbegin'){
4298                     before = el.firstChild;
4299                 }
4300                 node = ieTable(4, trs, html, tre);
4301             }
4302         } else if(tag == 'tbody'){
4303             if(where == 'beforebegin'){
4304                 before = el;
4305                 el = el.parentNode;
4306                 node = ieTable(2, ts, html, te);
4307             } else if(where == 'afterend'){
4308                 before = el.nextSibling;
4309                 el = el.parentNode;
4310                 node = ieTable(2, ts, html, te);
4311             } else{
4312                 if(where == 'afterbegin'){
4313                     before = el.firstChild;
4314                 }
4315                 node = ieTable(3, tbs, html, tbe);
4316             }
4317         } else{ // TABLE
4318             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4319                 return;
4320             }
4321             if(where == 'afterbegin'){
4322                 before = el.firstChild;
4323             }
4324             node = ieTable(2, ts, html, te);
4325         }
4326         el.insertBefore(node, before);
4327         return node;
4328     };
4329
4330     return {
4331     /** True to force the use of DOM instead of html fragments @type Boolean */
4332     useDom : false,
4333
4334     /**
4335      * Returns the markup for the passed Element(s) config
4336      * @param {Object} o The Dom object spec (and children)
4337      * @return {String}
4338      */
4339     markup : function(o){
4340         return createHtml(o);
4341     },
4342
4343     /**
4344      * Applies a style specification to an element
4345      * @param {String/HTMLElement} el The element to apply styles to
4346      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4347      * a function which returns such a specification.
4348      */
4349     applyStyles : function(el, styles){
4350         if(styles){
4351            el = Roo.fly(el);
4352            if(typeof styles == "string"){
4353                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4354                var matches;
4355                while ((matches = re.exec(styles)) != null){
4356                    el.setStyle(matches[1], matches[2]);
4357                }
4358            }else if (typeof styles == "object"){
4359                for (var style in styles){
4360                   el.setStyle(style, styles[style]);
4361                }
4362            }else if (typeof styles == "function"){
4363                 Roo.DomHelper.applyStyles(el, styles.call());
4364            }
4365         }
4366     },
4367
4368     /**
4369      * Inserts an HTML fragment into the Dom
4370      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4371      * @param {HTMLElement} el The context element
4372      * @param {String} html The HTML fragmenet
4373      * @return {HTMLElement} The new node
4374      */
4375     insertHtml : function(where, el, html){
4376         where = where.toLowerCase();
4377         if(el.insertAdjacentHTML){
4378             if(tableRe.test(el.tagName)){
4379                 var rs;
4380                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4381                     return rs;
4382                 }
4383             }
4384             switch(where){
4385                 case "beforebegin":
4386                     el.insertAdjacentHTML('BeforeBegin', html);
4387                     return el.previousSibling;
4388                 case "afterbegin":
4389                     el.insertAdjacentHTML('AfterBegin', html);
4390                     return el.firstChild;
4391                 case "beforeend":
4392                     el.insertAdjacentHTML('BeforeEnd', html);
4393                     return el.lastChild;
4394                 case "afterend":
4395                     el.insertAdjacentHTML('AfterEnd', html);
4396                     return el.nextSibling;
4397             }
4398             throw 'Illegal insertion point -> "' + where + '"';
4399         }
4400         var range = el.ownerDocument.createRange();
4401         var frag;
4402         switch(where){
4403              case "beforebegin":
4404                 range.setStartBefore(el);
4405                 frag = range.createContextualFragment(html);
4406                 el.parentNode.insertBefore(frag, el);
4407                 return el.previousSibling;
4408              case "afterbegin":
4409                 if(el.firstChild){
4410                     range.setStartBefore(el.firstChild);
4411                     frag = range.createContextualFragment(html);
4412                     el.insertBefore(frag, el.firstChild);
4413                     return el.firstChild;
4414                 }else{
4415                     el.innerHTML = html;
4416                     return el.firstChild;
4417                 }
4418             case "beforeend":
4419                 if(el.lastChild){
4420                     range.setStartAfter(el.lastChild);
4421                     frag = range.createContextualFragment(html);
4422                     el.appendChild(frag);
4423                     return el.lastChild;
4424                 }else{
4425                     el.innerHTML = html;
4426                     return el.lastChild;
4427                 }
4428             case "afterend":
4429                 range.setStartAfter(el);
4430                 frag = range.createContextualFragment(html);
4431                 el.parentNode.insertBefore(frag, el.nextSibling);
4432                 return el.nextSibling;
4433             }
4434             throw 'Illegal insertion point -> "' + where + '"';
4435     },
4436
4437     /**
4438      * Creates new Dom element(s) and inserts them before el
4439      * @param {String/HTMLElement/Element} el The context element
4440      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4441      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4442      * @return {HTMLElement/Roo.Element} The new node
4443      */
4444     insertBefore : function(el, o, returnElement){
4445         return this.doInsert(el, o, returnElement, "beforeBegin");
4446     },
4447
4448     /**
4449      * Creates new Dom element(s) and inserts them after el
4450      * @param {String/HTMLElement/Element} el The context element
4451      * @param {Object} o The Dom object spec (and children)
4452      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4453      * @return {HTMLElement/Roo.Element} The new node
4454      */
4455     insertAfter : function(el, o, returnElement){
4456         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4457     },
4458
4459     /**
4460      * Creates new Dom element(s) and inserts them as the first child of el
4461      * @param {String/HTMLElement/Element} el The context element
4462      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4463      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4464      * @return {HTMLElement/Roo.Element} The new node
4465      */
4466     insertFirst : function(el, o, returnElement){
4467         return this.doInsert(el, o, returnElement, "afterBegin");
4468     },
4469
4470     // private
4471     doInsert : function(el, o, returnElement, pos, sibling){
4472         el = Roo.getDom(el);
4473         var newNode;
4474         if(this.useDom || o.ns){
4475             newNode = createDom(o, null);
4476             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4477         }else{
4478             var html = createHtml(o);
4479             newNode = this.insertHtml(pos, el, html);
4480         }
4481         return returnElement ? Roo.get(newNode, true) : newNode;
4482     },
4483
4484     /**
4485      * Creates new Dom element(s) and appends them to el
4486      * @param {String/HTMLElement/Element} el The context element
4487      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4488      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4489      * @return {HTMLElement/Roo.Element} The new node
4490      */
4491     append : function(el, o, returnElement){
4492         el = Roo.getDom(el);
4493         var newNode;
4494         if(this.useDom || o.ns){
4495             newNode = createDom(o, null);
4496             el.appendChild(newNode);
4497         }else{
4498             var html = createHtml(o);
4499             newNode = this.insertHtml("beforeEnd", el, html);
4500         }
4501         return returnElement ? Roo.get(newNode, true) : newNode;
4502     },
4503
4504     /**
4505      * Creates new Dom element(s) and overwrites the contents of el with them
4506      * @param {String/HTMLElement/Element} el The context element
4507      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4508      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4509      * @return {HTMLElement/Roo.Element} The new node
4510      */
4511     overwrite : function(el, o, returnElement){
4512         el = Roo.getDom(el);
4513         if (o.ns) {
4514           
4515             while (el.childNodes.length) {
4516                 el.removeChild(el.firstChild);
4517             }
4518             createDom(o, el);
4519         } else {
4520             el.innerHTML = createHtml(o);   
4521         }
4522         
4523         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4524     },
4525
4526     /**
4527      * Creates a new Roo.DomHelper.Template from the Dom object spec
4528      * @param {Object} o The Dom object spec (and children)
4529      * @return {Roo.DomHelper.Template} The new template
4530      */
4531     createTemplate : function(o){
4532         var html = createHtml(o);
4533         return new Roo.Template(html);
4534     }
4535     };
4536 }();
4537 /*
4538  * Based on:
4539  * Ext JS Library 1.1.1
4540  * Copyright(c) 2006-2007, Ext JS, LLC.
4541  *
4542  * Originally Released Under LGPL - original licence link has changed is not relivant.
4543  *
4544  * Fork - LGPL
4545  * <script type="text/javascript">
4546  */
4547  
4548 /**
4549 * @class Roo.Template
4550 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4551 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4552 * Usage:
4553 <pre><code>
4554 var t = new Roo.Template({
4555     html :  '&lt;div name="{id}"&gt;' + 
4556         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4557         '&lt;/div&gt;',
4558     myformat: function (value, allValues) {
4559         return 'XX' + value;
4560     }
4561 });
4562 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4563 </code></pre>
4564 * For more information see this blog post with examples:
4565 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4566      - Create Elements using DOM, HTML fragments and Templates</a>. 
4567 * @constructor
4568 * @param {Object} cfg - Configuration object.
4569 */
4570 Roo.Template = function(cfg){
4571     // BC!
4572     if(cfg instanceof Array){
4573         cfg = cfg.join("");
4574     }else if(arguments.length > 1){
4575         cfg = Array.prototype.join.call(arguments, "");
4576     }
4577     
4578     
4579     if (typeof(cfg) == 'object') {
4580         Roo.apply(this,cfg)
4581     } else {
4582         // bc
4583         this.html = cfg;
4584     }
4585     if (this.url) {
4586         this.load();
4587     }
4588     
4589 };
4590 Roo.Template.prototype = {
4591     
4592     /**
4593      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4594      *                    it should be fixed so that template is observable...
4595      */
4596     url : false,
4597     /**
4598      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4599      */
4600     html : '',
4601     /**
4602      * Returns an HTML fragment of this template with the specified values applied.
4603      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4604      * @return {String} The HTML fragment
4605      */
4606     applyTemplate : function(values){
4607         try {
4608            
4609             if(this.compiled){
4610                 return this.compiled(values);
4611             }
4612             var useF = this.disableFormats !== true;
4613             var fm = Roo.util.Format, tpl = this;
4614             var fn = function(m, name, format, args){
4615                 if(format && useF){
4616                     if(format.substr(0, 5) == "this."){
4617                         return tpl.call(format.substr(5), values[name], values);
4618                     }else{
4619                         if(args){
4620                             // quoted values are required for strings in compiled templates, 
4621                             // but for non compiled we need to strip them
4622                             // quoted reversed for jsmin
4623                             var re = /^\s*['"](.*)["']\s*$/;
4624                             args = args.split(',');
4625                             for(var i = 0, len = args.length; i < len; i++){
4626                                 args[i] = args[i].replace(re, "$1");
4627                             }
4628                             args = [values[name]].concat(args);
4629                         }else{
4630                             args = [values[name]];
4631                         }
4632                         return fm[format].apply(fm, args);
4633                     }
4634                 }else{
4635                     return values[name] !== undefined ? values[name] : "";
4636                 }
4637             };
4638             return this.html.replace(this.re, fn);
4639         } catch (e) {
4640             Roo.log(e);
4641             throw e;
4642         }
4643          
4644     },
4645     
4646     loading : false,
4647       
4648     load : function ()
4649     {
4650          
4651         if (this.loading) {
4652             return;
4653         }
4654         var _t = this;
4655         
4656         this.loading = true;
4657         this.compiled = false;
4658         
4659         var cx = new Roo.data.Connection();
4660         cx.request({
4661             url : this.url,
4662             method : 'GET',
4663             success : function (response) {
4664                 _t.loading = false;
4665                 _t.html = response.responseText;
4666                 _t.url = false;
4667                 _t.compile();
4668              },
4669             failure : function(response) {
4670                 Roo.log("Template failed to load from " + _t.url);
4671                 _t.loading = false;
4672             }
4673         });
4674     },
4675
4676     /**
4677      * Sets the HTML used as the template and optionally compiles it.
4678      * @param {String} html
4679      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4680      * @return {Roo.Template} this
4681      */
4682     set : function(html, compile){
4683         this.html = html;
4684         this.compiled = null;
4685         if(compile){
4686             this.compile();
4687         }
4688         return this;
4689     },
4690     
4691     /**
4692      * True to disable format functions (defaults to false)
4693      * @type Boolean
4694      */
4695     disableFormats : false,
4696     
4697     /**
4698     * The regular expression used to match template variables 
4699     * @type RegExp
4700     * @property 
4701     */
4702     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4703     
4704     /**
4705      * Compiles the template into an internal function, eliminating the RegEx overhead.
4706      * @return {Roo.Template} this
4707      */
4708     compile : function(){
4709         var fm = Roo.util.Format;
4710         var useF = this.disableFormats !== true;
4711         var sep = Roo.isGecko ? "+" : ",";
4712         var fn = function(m, name, format, args){
4713             if(format && useF){
4714                 args = args ? ',' + args : "";
4715                 if(format.substr(0, 5) != "this."){
4716                     format = "fm." + format + '(';
4717                 }else{
4718                     format = 'this.call("'+ format.substr(5) + '", ';
4719                     args = ", values";
4720                 }
4721             }else{
4722                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4723             }
4724             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4725         };
4726         var body;
4727         // branched to use + in gecko and [].join() in others
4728         if(Roo.isGecko){
4729             body = "this.compiled = function(values){ return '" +
4730                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4731                     "';};";
4732         }else{
4733             body = ["this.compiled = function(values){ return ['"];
4734             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4735             body.push("'].join('');};");
4736             body = body.join('');
4737         }
4738         /**
4739          * eval:var:values
4740          * eval:var:fm
4741          */
4742         eval(body);
4743         return this;
4744     },
4745     
4746     // private function used to call members
4747     call : function(fnName, value, allValues){
4748         return this[fnName](value, allValues);
4749     },
4750     
4751     /**
4752      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4753      * @param {String/HTMLElement/Roo.Element} el The context element
4754      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4755      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4756      * @return {HTMLElement/Roo.Element} The new node or Element
4757      */
4758     insertFirst: function(el, values, returnElement){
4759         return this.doInsert('afterBegin', el, values, returnElement);
4760     },
4761
4762     /**
4763      * Applies the supplied values to the template and inserts the new node(s) before el.
4764      * @param {String/HTMLElement/Roo.Element} el The context element
4765      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4766      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4767      * @return {HTMLElement/Roo.Element} The new node or Element
4768      */
4769     insertBefore: function(el, values, returnElement){
4770         return this.doInsert('beforeBegin', el, values, returnElement);
4771     },
4772
4773     /**
4774      * Applies the supplied values to the template and inserts the new node(s) after el.
4775      * @param {String/HTMLElement/Roo.Element} el The context element
4776      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4777      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4778      * @return {HTMLElement/Roo.Element} The new node or Element
4779      */
4780     insertAfter : function(el, values, returnElement){
4781         return this.doInsert('afterEnd', el, values, returnElement);
4782     },
4783     
4784     /**
4785      * Applies the supplied values to the template and appends the new node(s) to el.
4786      * @param {String/HTMLElement/Roo.Element} el The context element
4787      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4788      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4789      * @return {HTMLElement/Roo.Element} The new node or Element
4790      */
4791     append : function(el, values, returnElement){
4792         return this.doInsert('beforeEnd', el, values, returnElement);
4793     },
4794
4795     doInsert : function(where, el, values, returnEl){
4796         el = Roo.getDom(el);
4797         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4798         return returnEl ? Roo.get(newNode, true) : newNode;
4799     },
4800
4801     /**
4802      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4803      * @param {String/HTMLElement/Roo.Element} el The context element
4804      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4805      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4806      * @return {HTMLElement/Roo.Element} The new node or Element
4807      */
4808     overwrite : function(el, values, returnElement){
4809         el = Roo.getDom(el);
4810         el.innerHTML = this.applyTemplate(values);
4811         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4812     }
4813 };
4814 /**
4815  * Alias for {@link #applyTemplate}
4816  * @method
4817  */
4818 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4819
4820 // backwards compat
4821 Roo.DomHelper.Template = Roo.Template;
4822
4823 /**
4824  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4825  * @param {String/HTMLElement} el A DOM element or its id
4826  * @returns {Roo.Template} The created template
4827  * @static
4828  */
4829 Roo.Template.from = function(el){
4830     el = Roo.getDom(el);
4831     return new Roo.Template(el.value || el.innerHTML);
4832 };/*
4833  * Based on:
4834  * Ext JS Library 1.1.1
4835  * Copyright(c) 2006-2007, Ext JS, LLC.
4836  *
4837  * Originally Released Under LGPL - original licence link has changed is not relivant.
4838  *
4839  * Fork - LGPL
4840  * <script type="text/javascript">
4841  */
4842  
4843
4844 /*
4845  * This is code is also distributed under MIT license for use
4846  * with jQuery and prototype JavaScript libraries.
4847  */
4848 /**
4849  * @class Roo.DomQuery
4850 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4851 <p>
4852 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4853
4854 <p>
4855 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4856 </p>
4857 <h4>Element Selectors:</h4>
4858 <ul class="list">
4859     <li> <b>*</b> any element</li>
4860     <li> <b>E</b> an element with the tag E</li>
4861     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4862     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4863     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4864     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4865 </ul>
4866 <h4>Attribute Selectors:</h4>
4867 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4868 <ul class="list">
4869     <li> <b>E[foo]</b> has an attribute "foo"</li>
4870     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4871     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4872     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4873     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4874     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4875     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4876 </ul>
4877 <h4>Pseudo Classes:</h4>
4878 <ul class="list">
4879     <li> <b>E:first-child</b> E is the first child of its parent</li>
4880     <li> <b>E:last-child</b> E is the last child of its parent</li>
4881     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4882     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4883     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4884     <li> <b>E:only-child</b> E is the only child of its parent</li>
4885     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4886     <li> <b>E:first</b> the first E in the resultset</li>
4887     <li> <b>E:last</b> the last E in the resultset</li>
4888     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4889     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4890     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4891     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4892     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4893     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4894     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4895     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4896     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4897 </ul>
4898 <h4>CSS Value Selectors:</h4>
4899 <ul class="list">
4900     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4901     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4902     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4903     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4904     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4905     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4906 </ul>
4907  * @singleton
4908  */
4909 Roo.DomQuery = function(){
4910     var cache = {}, simpleCache = {}, valueCache = {};
4911     var nonSpace = /\S/;
4912     var trimRe = /^\s+|\s+$/g;
4913     var tplRe = /\{(\d+)\}/g;
4914     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4915     var tagTokenRe = /^(#)?([\w-\*]+)/;
4916     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4917
4918     function child(p, index){
4919         var i = 0;
4920         var n = p.firstChild;
4921         while(n){
4922             if(n.nodeType == 1){
4923                if(++i == index){
4924                    return n;
4925                }
4926             }
4927             n = n.nextSibling;
4928         }
4929         return null;
4930     };
4931
4932     function next(n){
4933         while((n = n.nextSibling) && n.nodeType != 1);
4934         return n;
4935     };
4936
4937     function prev(n){
4938         while((n = n.previousSibling) && n.nodeType != 1);
4939         return n;
4940     };
4941
4942     function children(d){
4943         var n = d.firstChild, ni = -1;
4944             while(n){
4945                 var nx = n.nextSibling;
4946                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4947                     d.removeChild(n);
4948                 }else{
4949                     n.nodeIndex = ++ni;
4950                 }
4951                 n = nx;
4952             }
4953             return this;
4954         };
4955
4956     function byClassName(c, a, v){
4957         if(!v){
4958             return c;
4959         }
4960         var r = [], ri = -1, cn;
4961         for(var i = 0, ci; ci = c[i]; i++){
4962             if((' '+ci.className+' ').indexOf(v) != -1){
4963                 r[++ri] = ci;
4964             }
4965         }
4966         return r;
4967     };
4968
4969     function attrValue(n, attr){
4970         if(!n.tagName && typeof n.length != "undefined"){
4971             n = n[0];
4972         }
4973         if(!n){
4974             return null;
4975         }
4976         if(attr == "for"){
4977             return n.htmlFor;
4978         }
4979         if(attr == "class" || attr == "className"){
4980             return n.className;
4981         }
4982         return n.getAttribute(attr) || n[attr];
4983
4984     };
4985
4986     function getNodes(ns, mode, tagName){
4987         var result = [], ri = -1, cs;
4988         if(!ns){
4989             return result;
4990         }
4991         tagName = tagName || "*";
4992         if(typeof ns.getElementsByTagName != "undefined"){
4993             ns = [ns];
4994         }
4995         if(!mode){
4996             for(var i = 0, ni; ni = ns[i]; i++){
4997                 cs = ni.getElementsByTagName(tagName);
4998                 for(var j = 0, ci; ci = cs[j]; j++){
4999                     result[++ri] = ci;
5000                 }
5001             }
5002         }else if(mode == "/" || mode == ">"){
5003             var utag = tagName.toUpperCase();
5004             for(var i = 0, ni, cn; ni = ns[i]; i++){
5005                 cn = ni.children || ni.childNodes;
5006                 for(var j = 0, cj; cj = cn[j]; j++){
5007                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5008                         result[++ri] = cj;
5009                     }
5010                 }
5011             }
5012         }else if(mode == "+"){
5013             var utag = tagName.toUpperCase();
5014             for(var i = 0, n; n = ns[i]; i++){
5015                 while((n = n.nextSibling) && n.nodeType != 1);
5016                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5017                     result[++ri] = n;
5018                 }
5019             }
5020         }else if(mode == "~"){
5021             for(var i = 0, n; n = ns[i]; i++){
5022                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5023                 if(n){
5024                     result[++ri] = n;
5025                 }
5026             }
5027         }
5028         return result;
5029     };
5030
5031     function concat(a, b){
5032         if(b.slice){
5033             return a.concat(b);
5034         }
5035         for(var i = 0, l = b.length; i < l; i++){
5036             a[a.length] = b[i];
5037         }
5038         return a;
5039     }
5040
5041     function byTag(cs, tagName){
5042         if(cs.tagName || cs == document){
5043             cs = [cs];
5044         }
5045         if(!tagName){
5046             return cs;
5047         }
5048         var r = [], ri = -1;
5049         tagName = tagName.toLowerCase();
5050         for(var i = 0, ci; ci = cs[i]; i++){
5051             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5052                 r[++ri] = ci;
5053             }
5054         }
5055         return r;
5056     };
5057
5058     function byId(cs, attr, id){
5059         if(cs.tagName || cs == document){
5060             cs = [cs];
5061         }
5062         if(!id){
5063             return cs;
5064         }
5065         var r = [], ri = -1;
5066         for(var i = 0,ci; ci = cs[i]; i++){
5067             if(ci && ci.id == id){
5068                 r[++ri] = ci;
5069                 return r;
5070             }
5071         }
5072         return r;
5073     };
5074
5075     function byAttribute(cs, attr, value, op, custom){
5076         var r = [], ri = -1, st = custom=="{";
5077         var f = Roo.DomQuery.operators[op];
5078         for(var i = 0, ci; ci = cs[i]; i++){
5079             var a;
5080             if(st){
5081                 a = Roo.DomQuery.getStyle(ci, attr);
5082             }
5083             else if(attr == "class" || attr == "className"){
5084                 a = ci.className;
5085             }else if(attr == "for"){
5086                 a = ci.htmlFor;
5087             }else if(attr == "href"){
5088                 a = ci.getAttribute("href", 2);
5089             }else{
5090                 a = ci.getAttribute(attr);
5091             }
5092             if((f && f(a, value)) || (!f && a)){
5093                 r[++ri] = ci;
5094             }
5095         }
5096         return r;
5097     };
5098
5099     function byPseudo(cs, name, value){
5100         return Roo.DomQuery.pseudos[name](cs, value);
5101     };
5102
5103     // This is for IE MSXML which does not support expandos.
5104     // IE runs the same speed using setAttribute, however FF slows way down
5105     // and Safari completely fails so they need to continue to use expandos.
5106     var isIE = window.ActiveXObject ? true : false;
5107
5108     // this eval is stop the compressor from
5109     // renaming the variable to something shorter
5110     
5111     /** eval:var:batch */
5112     var batch = 30803; 
5113
5114     var key = 30803;
5115
5116     function nodupIEXml(cs){
5117         var d = ++key;
5118         cs[0].setAttribute("_nodup", d);
5119         var r = [cs[0]];
5120         for(var i = 1, len = cs.length; i < len; i++){
5121             var c = cs[i];
5122             if(!c.getAttribute("_nodup") != d){
5123                 c.setAttribute("_nodup", d);
5124                 r[r.length] = c;
5125             }
5126         }
5127         for(var i = 0, len = cs.length; i < len; i++){
5128             cs[i].removeAttribute("_nodup");
5129         }
5130         return r;
5131     }
5132
5133     function nodup(cs){
5134         if(!cs){
5135             return [];
5136         }
5137         var len = cs.length, c, i, r = cs, cj, ri = -1;
5138         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5139             return cs;
5140         }
5141         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5142             return nodupIEXml(cs);
5143         }
5144         var d = ++key;
5145         cs[0]._nodup = d;
5146         for(i = 1; c = cs[i]; i++){
5147             if(c._nodup != d){
5148                 c._nodup = d;
5149             }else{
5150                 r = [];
5151                 for(var j = 0; j < i; j++){
5152                     r[++ri] = cs[j];
5153                 }
5154                 for(j = i+1; cj = cs[j]; j++){
5155                     if(cj._nodup != d){
5156                         cj._nodup = d;
5157                         r[++ri] = cj;
5158                     }
5159                 }
5160                 return r;
5161             }
5162         }
5163         return r;
5164     }
5165
5166     function quickDiffIEXml(c1, c2){
5167         var d = ++key;
5168         for(var i = 0, len = c1.length; i < len; i++){
5169             c1[i].setAttribute("_qdiff", d);
5170         }
5171         var r = [];
5172         for(var i = 0, len = c2.length; i < len; i++){
5173             if(c2[i].getAttribute("_qdiff") != d){
5174                 r[r.length] = c2[i];
5175             }
5176         }
5177         for(var i = 0, len = c1.length; i < len; i++){
5178            c1[i].removeAttribute("_qdiff");
5179         }
5180         return r;
5181     }
5182
5183     function quickDiff(c1, c2){
5184         var len1 = c1.length;
5185         if(!len1){
5186             return c2;
5187         }
5188         if(isIE && c1[0].selectSingleNode){
5189             return quickDiffIEXml(c1, c2);
5190         }
5191         var d = ++key;
5192         for(var i = 0; i < len1; i++){
5193             c1[i]._qdiff = d;
5194         }
5195         var r = [];
5196         for(var i = 0, len = c2.length; i < len; i++){
5197             if(c2[i]._qdiff != d){
5198                 r[r.length] = c2[i];
5199             }
5200         }
5201         return r;
5202     }
5203
5204     function quickId(ns, mode, root, id){
5205         if(ns == root){
5206            var d = root.ownerDocument || root;
5207            return d.getElementById(id);
5208         }
5209         ns = getNodes(ns, mode, "*");
5210         return byId(ns, null, id);
5211     }
5212
5213     return {
5214         getStyle : function(el, name){
5215             return Roo.fly(el).getStyle(name);
5216         },
5217         /**
5218          * Compiles a selector/xpath query into a reusable function. The returned function
5219          * takes one parameter "root" (optional), which is the context node from where the query should start.
5220          * @param {String} selector The selector/xpath query
5221          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5222          * @return {Function}
5223          */
5224         compile : function(path, type){
5225             type = type || "select";
5226             
5227             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5228             var q = path, mode, lq;
5229             var tk = Roo.DomQuery.matchers;
5230             var tklen = tk.length;
5231             var mm;
5232
5233             // accept leading mode switch
5234             var lmode = q.match(modeRe);
5235             if(lmode && lmode[1]){
5236                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5237                 q = q.replace(lmode[1], "");
5238             }
5239             // strip leading slashes
5240             while(path.substr(0, 1)=="/"){
5241                 path = path.substr(1);
5242             }
5243
5244             while(q && lq != q){
5245                 lq = q;
5246                 var tm = q.match(tagTokenRe);
5247                 if(type == "select"){
5248                     if(tm){
5249                         if(tm[1] == "#"){
5250                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5251                         }else{
5252                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5253                         }
5254                         q = q.replace(tm[0], "");
5255                     }else if(q.substr(0, 1) != '@'){
5256                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5257                     }
5258                 }else{
5259                     if(tm){
5260                         if(tm[1] == "#"){
5261                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5262                         }else{
5263                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5264                         }
5265                         q = q.replace(tm[0], "");
5266                     }
5267                 }
5268                 while(!(mm = q.match(modeRe))){
5269                     var matched = false;
5270                     for(var j = 0; j < tklen; j++){
5271                         var t = tk[j];
5272                         var m = q.match(t.re);
5273                         if(m){
5274                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5275                                                     return m[i];
5276                                                 });
5277                             q = q.replace(m[0], "");
5278                             matched = true;
5279                             break;
5280                         }
5281                     }
5282                     // prevent infinite loop on bad selector
5283                     if(!matched){
5284                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5285                     }
5286                 }
5287                 if(mm[1]){
5288                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5289                     q = q.replace(mm[1], "");
5290                 }
5291             }
5292             fn[fn.length] = "return nodup(n);\n}";
5293             
5294              /** 
5295               * list of variables that need from compression as they are used by eval.
5296              *  eval:var:batch 
5297              *  eval:var:nodup
5298              *  eval:var:byTag
5299              *  eval:var:ById
5300              *  eval:var:getNodes
5301              *  eval:var:quickId
5302              *  eval:var:mode
5303              *  eval:var:root
5304              *  eval:var:n
5305              *  eval:var:byClassName
5306              *  eval:var:byPseudo
5307              *  eval:var:byAttribute
5308              *  eval:var:attrValue
5309              * 
5310              **/ 
5311             eval(fn.join(""));
5312             return f;
5313         },
5314
5315         /**
5316          * Selects a group of elements.
5317          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5318          * @param {Node} root (optional) The start of the query (defaults to document).
5319          * @return {Array}
5320          */
5321         select : function(path, root, type){
5322             if(!root || root == document){
5323                 root = document;
5324             }
5325             if(typeof root == "string"){
5326                 root = document.getElementById(root);
5327             }
5328             var paths = path.split(",");
5329             var results = [];
5330             for(var i = 0, len = paths.length; i < len; i++){
5331                 var p = paths[i].replace(trimRe, "");
5332                 if(!cache[p]){
5333                     cache[p] = Roo.DomQuery.compile(p);
5334                     if(!cache[p]){
5335                         throw p + " is not a valid selector";
5336                     }
5337                 }
5338                 var result = cache[p](root);
5339                 if(result && result != document){
5340                     results = results.concat(result);
5341                 }
5342             }
5343             if(paths.length > 1){
5344                 return nodup(results);
5345             }
5346             return results;
5347         },
5348
5349         /**
5350          * Selects a single element.
5351          * @param {String} selector The selector/xpath query
5352          * @param {Node} root (optional) The start of the query (defaults to document).
5353          * @return {Element}
5354          */
5355         selectNode : function(path, root){
5356             return Roo.DomQuery.select(path, root)[0];
5357         },
5358
5359         /**
5360          * Selects the value of a node, optionally replacing null with the defaultValue.
5361          * @param {String} selector The selector/xpath query
5362          * @param {Node} root (optional) The start of the query (defaults to document).
5363          * @param {String} defaultValue
5364          */
5365         selectValue : function(path, root, defaultValue){
5366             path = path.replace(trimRe, "");
5367             if(!valueCache[path]){
5368                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5369             }
5370             var n = valueCache[path](root);
5371             n = n[0] ? n[0] : n;
5372             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5373             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5374         },
5375
5376         /**
5377          * Selects the value of a node, parsing integers and floats.
5378          * @param {String} selector The selector/xpath query
5379          * @param {Node} root (optional) The start of the query (defaults to document).
5380          * @param {Number} defaultValue
5381          * @return {Number}
5382          */
5383         selectNumber : function(path, root, defaultValue){
5384             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5385             return parseFloat(v);
5386         },
5387
5388         /**
5389          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5390          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5391          * @param {String} selector The simple selector to test
5392          * @return {Boolean}
5393          */
5394         is : function(el, ss){
5395             if(typeof el == "string"){
5396                 el = document.getElementById(el);
5397             }
5398             var isArray = (el instanceof Array);
5399             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5400             return isArray ? (result.length == el.length) : (result.length > 0);
5401         },
5402
5403         /**
5404          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5405          * @param {Array} el An array of elements to filter
5406          * @param {String} selector The simple selector to test
5407          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5408          * the selector instead of the ones that match
5409          * @return {Array}
5410          */
5411         filter : function(els, ss, nonMatches){
5412             ss = ss.replace(trimRe, "");
5413             if(!simpleCache[ss]){
5414                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5415             }
5416             var result = simpleCache[ss](els);
5417             return nonMatches ? quickDiff(result, els) : result;
5418         },
5419
5420         /**
5421          * Collection of matching regular expressions and code snippets.
5422          */
5423         matchers : [{
5424                 re: /^\.([\w-]+)/,
5425                 select: 'n = byClassName(n, null, " {1} ");'
5426             }, {
5427                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5428                 select: 'n = byPseudo(n, "{1}", "{2}");'
5429             },{
5430                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5431                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5432             }, {
5433                 re: /^#([\w-]+)/,
5434                 select: 'n = byId(n, null, "{1}");'
5435             },{
5436                 re: /^@([\w-]+)/,
5437                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5438             }
5439         ],
5440
5441         /**
5442          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5443          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5444          */
5445         operators : {
5446             "=" : function(a, v){
5447                 return a == v;
5448             },
5449             "!=" : function(a, v){
5450                 return a != v;
5451             },
5452             "^=" : function(a, v){
5453                 return a && a.substr(0, v.length) == v;
5454             },
5455             "$=" : function(a, v){
5456                 return a && a.substr(a.length-v.length) == v;
5457             },
5458             "*=" : function(a, v){
5459                 return a && a.indexOf(v) !== -1;
5460             },
5461             "%=" : function(a, v){
5462                 return (a % v) == 0;
5463             },
5464             "|=" : function(a, v){
5465                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5466             },
5467             "~=" : function(a, v){
5468                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5469             }
5470         },
5471
5472         /**
5473          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5474          * and the argument (if any) supplied in the selector.
5475          */
5476         pseudos : {
5477             "first-child" : function(c){
5478                 var r = [], ri = -1, n;
5479                 for(var i = 0, ci; ci = n = c[i]; i++){
5480                     while((n = n.previousSibling) && n.nodeType != 1);
5481                     if(!n){
5482                         r[++ri] = ci;
5483                     }
5484                 }
5485                 return r;
5486             },
5487
5488             "last-child" : function(c){
5489                 var r = [], ri = -1, n;
5490                 for(var i = 0, ci; ci = n = c[i]; i++){
5491                     while((n = n.nextSibling) && n.nodeType != 1);
5492                     if(!n){
5493                         r[++ri] = ci;
5494                     }
5495                 }
5496                 return r;
5497             },
5498
5499             "nth-child" : function(c, a) {
5500                 var r = [], ri = -1;
5501                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5502                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5503                 for(var i = 0, n; n = c[i]; i++){
5504                     var pn = n.parentNode;
5505                     if (batch != pn._batch) {
5506                         var j = 0;
5507                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5508                             if(cn.nodeType == 1){
5509                                cn.nodeIndex = ++j;
5510                             }
5511                         }
5512                         pn._batch = batch;
5513                     }
5514                     if (f == 1) {
5515                         if (l == 0 || n.nodeIndex == l){
5516                             r[++ri] = n;
5517                         }
5518                     } else if ((n.nodeIndex + l) % f == 0){
5519                         r[++ri] = n;
5520                     }
5521                 }
5522
5523                 return r;
5524             },
5525
5526             "only-child" : function(c){
5527                 var r = [], ri = -1;;
5528                 for(var i = 0, ci; ci = c[i]; i++){
5529                     if(!prev(ci) && !next(ci)){
5530                         r[++ri] = ci;
5531                     }
5532                 }
5533                 return r;
5534             },
5535
5536             "empty" : function(c){
5537                 var r = [], ri = -1;
5538                 for(var i = 0, ci; ci = c[i]; i++){
5539                     var cns = ci.childNodes, j = 0, cn, empty = true;
5540                     while(cn = cns[j]){
5541                         ++j;
5542                         if(cn.nodeType == 1 || cn.nodeType == 3){
5543                             empty = false;
5544                             break;
5545                         }
5546                     }
5547                     if(empty){
5548                         r[++ri] = ci;
5549                     }
5550                 }
5551                 return r;
5552             },
5553
5554             "contains" : function(c, v){
5555                 var r = [], ri = -1;
5556                 for(var i = 0, ci; ci = c[i]; i++){
5557                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5558                         r[++ri] = ci;
5559                     }
5560                 }
5561                 return r;
5562             },
5563
5564             "nodeValue" : function(c, v){
5565                 var r = [], ri = -1;
5566                 for(var i = 0, ci; ci = c[i]; i++){
5567                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5568                         r[++ri] = ci;
5569                     }
5570                 }
5571                 return r;
5572             },
5573
5574             "checked" : function(c){
5575                 var r = [], ri = -1;
5576                 for(var i = 0, ci; ci = c[i]; i++){
5577                     if(ci.checked == true){
5578                         r[++ri] = ci;
5579                     }
5580                 }
5581                 return r;
5582             },
5583
5584             "not" : function(c, ss){
5585                 return Roo.DomQuery.filter(c, ss, true);
5586             },
5587
5588             "odd" : function(c){
5589                 return this["nth-child"](c, "odd");
5590             },
5591
5592             "even" : function(c){
5593                 return this["nth-child"](c, "even");
5594             },
5595
5596             "nth" : function(c, a){
5597                 return c[a-1] || [];
5598             },
5599
5600             "first" : function(c){
5601                 return c[0] || [];
5602             },
5603
5604             "last" : function(c){
5605                 return c[c.length-1] || [];
5606             },
5607
5608             "has" : function(c, ss){
5609                 var s = Roo.DomQuery.select;
5610                 var r = [], ri = -1;
5611                 for(var i = 0, ci; ci = c[i]; i++){
5612                     if(s(ss, ci).length > 0){
5613                         r[++ri] = ci;
5614                     }
5615                 }
5616                 return r;
5617             },
5618
5619             "next" : function(c, ss){
5620                 var is = Roo.DomQuery.is;
5621                 var r = [], ri = -1;
5622                 for(var i = 0, ci; ci = c[i]; i++){
5623                     var n = next(ci);
5624                     if(n && is(n, ss)){
5625                         r[++ri] = ci;
5626                     }
5627                 }
5628                 return r;
5629             },
5630
5631             "prev" : function(c, ss){
5632                 var is = Roo.DomQuery.is;
5633                 var r = [], ri = -1;
5634                 for(var i = 0, ci; ci = c[i]; i++){
5635                     var n = prev(ci);
5636                     if(n && is(n, ss)){
5637                         r[++ri] = ci;
5638                     }
5639                 }
5640                 return r;
5641             }
5642         }
5643     };
5644 }();
5645
5646 /**
5647  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5648  * @param {String} path The selector/xpath query
5649  * @param {Node} root (optional) The start of the query (defaults to document).
5650  * @return {Array}
5651  * @member Roo
5652  * @method query
5653  */
5654 Roo.query = Roo.DomQuery.select;
5655 /*
5656  * Based on:
5657  * Ext JS Library 1.1.1
5658  * Copyright(c) 2006-2007, Ext JS, LLC.
5659  *
5660  * Originally Released Under LGPL - original licence link has changed is not relivant.
5661  *
5662  * Fork - LGPL
5663  * <script type="text/javascript">
5664  */
5665
5666 /**
5667  * @class Roo.util.Observable
5668  * Base class that provides a common interface for publishing events. Subclasses are expected to
5669  * to have a property "events" with all the events defined.<br>
5670  * For example:
5671  * <pre><code>
5672  Employee = function(name){
5673     this.name = name;
5674     this.addEvents({
5675         "fired" : true,
5676         "quit" : true
5677     });
5678  }
5679  Roo.extend(Employee, Roo.util.Observable);
5680 </code></pre>
5681  * @param {Object} config properties to use (incuding events / listeners)
5682  */
5683
5684 Roo.util.Observable = function(cfg){
5685     
5686     cfg = cfg|| {};
5687     this.addEvents(cfg.events || {});
5688     if (cfg.events) {
5689         delete cfg.events; // make sure
5690     }
5691      
5692     Roo.apply(this, cfg);
5693     
5694     if(this.listeners){
5695         this.on(this.listeners);
5696         delete this.listeners;
5697     }
5698 };
5699 Roo.util.Observable.prototype = {
5700     /** 
5701  * @cfg {Object} listeners  list of events and functions to call for this object, 
5702  * For example :
5703  * <pre><code>
5704     listeners :  { 
5705        'click' : function(e) {
5706            ..... 
5707         } ,
5708         .... 
5709     } 
5710   </code></pre>
5711  */
5712     
5713     
5714     /**
5715      * Fires the specified event with the passed parameters (minus the event name).
5716      * @param {String} eventName
5717      * @param {Object...} args Variable number of parameters are passed to handlers
5718      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5719      */
5720     fireEvent : function(){
5721         var ce = this.events[arguments[0].toLowerCase()];
5722         if(typeof ce == "object"){
5723             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5724         }else{
5725             return true;
5726         }
5727     },
5728
5729     // private
5730     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5731
5732     /**
5733      * Appends an event handler to this component
5734      * @param {String}   eventName The type of event to listen for
5735      * @param {Function} handler The method the event invokes
5736      * @param {Object}   scope (optional) The scope in which to execute the handler
5737      * function. The handler function's "this" context.
5738      * @param {Object}   options (optional) An object containing handler configuration
5739      * properties. This may contain any of the following properties:<ul>
5740      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5741      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5742      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5743      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5744      * by the specified number of milliseconds. If the event fires again within that time, the original
5745      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5746      * </ul><br>
5747      * <p>
5748      * <b>Combining Options</b><br>
5749      * Using the options argument, it is possible to combine different types of listeners:<br>
5750      * <br>
5751      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5752                 <pre><code>
5753                 el.on('click', this.onClick, this, {
5754                         single: true,
5755                 delay: 100,
5756                 forumId: 4
5757                 });
5758                 </code></pre>
5759      * <p>
5760      * <b>Attaching multiple handlers in 1 call</b><br>
5761      * The method also allows for a single argument to be passed which is a config object containing properties
5762      * which specify multiple handlers.
5763      * <pre><code>
5764                 el.on({
5765                         'click': {
5766                         fn: this.onClick,
5767                         scope: this,
5768                         delay: 100
5769                 }, 
5770                 'mouseover': {
5771                         fn: this.onMouseOver,
5772                         scope: this
5773                 },
5774                 'mouseout': {
5775                         fn: this.onMouseOut,
5776                         scope: this
5777                 }
5778                 });
5779                 </code></pre>
5780      * <p>
5781      * Or a shorthand syntax which passes the same scope object to all handlers:
5782         <pre><code>
5783                 el.on({
5784                         'click': this.onClick,
5785                 'mouseover': this.onMouseOver,
5786                 'mouseout': this.onMouseOut,
5787                 scope: this
5788                 });
5789                 </code></pre>
5790      */
5791     addListener : function(eventName, fn, scope, o){
5792         if(typeof eventName == "object"){
5793             o = eventName;
5794             for(var e in o){
5795                 if(this.filterOptRe.test(e)){
5796                     continue;
5797                 }
5798                 if(typeof o[e] == "function"){
5799                     // shared options
5800                     this.addListener(e, o[e], o.scope,  o);
5801                 }else{
5802                     // individual options
5803                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5804                 }
5805             }
5806             return;
5807         }
5808         o = (!o || typeof o == "boolean") ? {} : o;
5809         eventName = eventName.toLowerCase();
5810         var ce = this.events[eventName] || true;
5811         if(typeof ce == "boolean"){
5812             ce = new Roo.util.Event(this, eventName);
5813             this.events[eventName] = ce;
5814         }
5815         ce.addListener(fn, scope, o);
5816     },
5817
5818     /**
5819      * Removes a listener
5820      * @param {String}   eventName     The type of event to listen for
5821      * @param {Function} handler        The handler to remove
5822      * @param {Object}   scope  (optional) The scope (this object) for the handler
5823      */
5824     removeListener : function(eventName, fn, scope){
5825         var ce = this.events[eventName.toLowerCase()];
5826         if(typeof ce == "object"){
5827             ce.removeListener(fn, scope);
5828         }
5829     },
5830
5831     /**
5832      * Removes all listeners for this object
5833      */
5834     purgeListeners : function(){
5835         for(var evt in this.events){
5836             if(typeof this.events[evt] == "object"){
5837                  this.events[evt].clearListeners();
5838             }
5839         }
5840     },
5841
5842     relayEvents : function(o, events){
5843         var createHandler = function(ename){
5844             return function(){
5845                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5846             };
5847         };
5848         for(var i = 0, len = events.length; i < len; i++){
5849             var ename = events[i];
5850             if(!this.events[ename]){ this.events[ename] = true; };
5851             o.on(ename, createHandler(ename), this);
5852         }
5853     },
5854
5855     /**
5856      * Used to define events on this Observable
5857      * @param {Object} object The object with the events defined
5858      */
5859     addEvents : function(o){
5860         if(!this.events){
5861             this.events = {};
5862         }
5863         Roo.applyIf(this.events, o);
5864     },
5865
5866     /**
5867      * Checks to see if this object has any listeners for a specified event
5868      * @param {String} eventName The name of the event to check for
5869      * @return {Boolean} True if the event is being listened for, else false
5870      */
5871     hasListener : function(eventName){
5872         var e = this.events[eventName];
5873         return typeof e == "object" && e.listeners.length > 0;
5874     }
5875 };
5876 /**
5877  * Appends an event handler to this element (shorthand for addListener)
5878  * @param {String}   eventName     The type of event to listen for
5879  * @param {Function} handler        The method the event invokes
5880  * @param {Object}   scope (optional) The scope in which to execute the handler
5881  * function. The handler function's "this" context.
5882  * @param {Object}   options  (optional)
5883  * @method
5884  */
5885 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5886 /**
5887  * Removes a listener (shorthand for removeListener)
5888  * @param {String}   eventName     The type of event to listen for
5889  * @param {Function} handler        The handler to remove
5890  * @param {Object}   scope  (optional) The scope (this object) for the handler
5891  * @method
5892  */
5893 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5894
5895 /**
5896  * Starts capture on the specified Observable. All events will be passed
5897  * to the supplied function with the event name + standard signature of the event
5898  * <b>before</b> the event is fired. If the supplied function returns false,
5899  * the event will not fire.
5900  * @param {Observable} o The Observable to capture
5901  * @param {Function} fn The function to call
5902  * @param {Object} scope (optional) The scope (this object) for the fn
5903  * @static
5904  */
5905 Roo.util.Observable.capture = function(o, fn, scope){
5906     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5907 };
5908
5909 /**
5910  * Removes <b>all</b> added captures from the Observable.
5911  * @param {Observable} o The Observable to release
5912  * @static
5913  */
5914 Roo.util.Observable.releaseCapture = function(o){
5915     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5916 };
5917
5918 (function(){
5919
5920     var createBuffered = function(h, o, scope){
5921         var task = new Roo.util.DelayedTask();
5922         return function(){
5923             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5924         };
5925     };
5926
5927     var createSingle = function(h, e, fn, scope){
5928         return function(){
5929             e.removeListener(fn, scope);
5930             return h.apply(scope, arguments);
5931         };
5932     };
5933
5934     var createDelayed = function(h, o, scope){
5935         return function(){
5936             var args = Array.prototype.slice.call(arguments, 0);
5937             setTimeout(function(){
5938                 h.apply(scope, args);
5939             }, o.delay || 10);
5940         };
5941     };
5942
5943     Roo.util.Event = function(obj, name){
5944         this.name = name;
5945         this.obj = obj;
5946         this.listeners = [];
5947     };
5948
5949     Roo.util.Event.prototype = {
5950         addListener : function(fn, scope, options){
5951             var o = options || {};
5952             scope = scope || this.obj;
5953             if(!this.isListening(fn, scope)){
5954                 var l = {fn: fn, scope: scope, options: o};
5955                 var h = fn;
5956                 if(o.delay){
5957                     h = createDelayed(h, o, scope);
5958                 }
5959                 if(o.single){
5960                     h = createSingle(h, this, fn, scope);
5961                 }
5962                 if(o.buffer){
5963                     h = createBuffered(h, o, scope);
5964                 }
5965                 l.fireFn = h;
5966                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5967                     this.listeners.push(l);
5968                 }else{
5969                     this.listeners = this.listeners.slice(0);
5970                     this.listeners.push(l);
5971                 }
5972             }
5973         },
5974
5975         findListener : function(fn, scope){
5976             scope = scope || this.obj;
5977             var ls = this.listeners;
5978             for(var i = 0, len = ls.length; i < len; i++){
5979                 var l = ls[i];
5980                 if(l.fn == fn && l.scope == scope){
5981                     return i;
5982                 }
5983             }
5984             return -1;
5985         },
5986
5987         isListening : function(fn, scope){
5988             return this.findListener(fn, scope) != -1;
5989         },
5990
5991         removeListener : function(fn, scope){
5992             var index;
5993             if((index = this.findListener(fn, scope)) != -1){
5994                 if(!this.firing){
5995                     this.listeners.splice(index, 1);
5996                 }else{
5997                     this.listeners = this.listeners.slice(0);
5998                     this.listeners.splice(index, 1);
5999                 }
6000                 return true;
6001             }
6002             return false;
6003         },
6004
6005         clearListeners : function(){
6006             this.listeners = [];
6007         },
6008
6009         fire : function(){
6010             var ls = this.listeners, scope, len = ls.length;
6011             if(len > 0){
6012                 this.firing = true;
6013                 var args = Array.prototype.slice.call(arguments, 0);
6014                 for(var i = 0; i < len; i++){
6015                     var l = ls[i];
6016                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6017                         this.firing = false;
6018                         return false;
6019                     }
6020                 }
6021                 this.firing = false;
6022             }
6023             return true;
6024         }
6025     };
6026 })();/*
6027  * Based on:
6028  * Ext JS Library 1.1.1
6029  * Copyright(c) 2006-2007, Ext JS, LLC.
6030  *
6031  * Originally Released Under LGPL - original licence link has changed is not relivant.
6032  *
6033  * Fork - LGPL
6034  * <script type="text/javascript">
6035  */
6036
6037 /**
6038  * @class Roo.EventManager
6039  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6040  * several useful events directly.
6041  * See {@link Roo.EventObject} for more details on normalized event objects.
6042  * @singleton
6043  */
6044 Roo.EventManager = function(){
6045     var docReadyEvent, docReadyProcId, docReadyState = false;
6046     var resizeEvent, resizeTask, textEvent, textSize;
6047     var E = Roo.lib.Event;
6048     var D = Roo.lib.Dom;
6049
6050     
6051     
6052
6053     var fireDocReady = function(){
6054         if(!docReadyState){
6055             docReadyState = true;
6056             Roo.isReady = true;
6057             if(docReadyProcId){
6058                 clearInterval(docReadyProcId);
6059             }
6060             if(Roo.isGecko || Roo.isOpera) {
6061                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6062             }
6063             if(Roo.isIE){
6064                 var defer = document.getElementById("ie-deferred-loader");
6065                 if(defer){
6066                     defer.onreadystatechange = null;
6067                     defer.parentNode.removeChild(defer);
6068                 }
6069             }
6070             if(docReadyEvent){
6071                 docReadyEvent.fire();
6072                 docReadyEvent.clearListeners();
6073             }
6074         }
6075     };
6076     
6077     var initDocReady = function(){
6078         docReadyEvent = new Roo.util.Event();
6079         if(Roo.isGecko || Roo.isOpera) {
6080             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6081         }else if(Roo.isIE){
6082             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6083             var defer = document.getElementById("ie-deferred-loader");
6084             defer.onreadystatechange = function(){
6085                 if(this.readyState == "complete"){
6086                     fireDocReady();
6087                 }
6088             };
6089         }else if(Roo.isSafari){ 
6090             docReadyProcId = setInterval(function(){
6091                 var rs = document.readyState;
6092                 if(rs == "complete") {
6093                     fireDocReady();     
6094                  }
6095             }, 10);
6096         }
6097         // no matter what, make sure it fires on load
6098         E.on(window, "load", fireDocReady);
6099     };
6100
6101     var createBuffered = function(h, o){
6102         var task = new Roo.util.DelayedTask(h);
6103         return function(e){
6104             // create new event object impl so new events don't wipe out properties
6105             e = new Roo.EventObjectImpl(e);
6106             task.delay(o.buffer, h, null, [e]);
6107         };
6108     };
6109
6110     var createSingle = function(h, el, ename, fn){
6111         return function(e){
6112             Roo.EventManager.removeListener(el, ename, fn);
6113             h(e);
6114         };
6115     };
6116
6117     var createDelayed = function(h, o){
6118         return function(e){
6119             // create new event object impl so new events don't wipe out properties
6120             e = new Roo.EventObjectImpl(e);
6121             setTimeout(function(){
6122                 h(e);
6123             }, o.delay || 10);
6124         };
6125     };
6126     var transitionEndVal = false;
6127     
6128     var transitionEnd = function()
6129     {
6130         if (transitionEndVal) {
6131             return transitionEndVal;
6132         }
6133         var el = document.createElement('div')
6134
6135         var transEndEventNames = {
6136             WebkitTransition : 'webkitTransitionEnd',
6137             MozTransition    : 'transitionend',
6138             OTransition      : 'oTransitionEnd otransitionend',
6139             transition       : 'transitionend'
6140         }
6141     
6142         for (var name in transEndEventNames) {
6143             if (el.style[name] !== undefined) {
6144                 transitionEndVal = transEndEventNames[name];
6145                 return  transitionEndVal ;
6146             }
6147         }
6148     }
6149     
6150
6151     var listen = function(element, ename, opt, fn, scope){
6152         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6153         fn = fn || o.fn; scope = scope || o.scope;
6154         var el = Roo.getDom(element);
6155         
6156         
6157         if(!el){
6158             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6159         }
6160         
6161         if (ename == 'transitionend') {
6162             ename = transitionEnd();
6163         }
6164         var h = function(e){
6165             e = Roo.EventObject.setEvent(e);
6166             var t;
6167             if(o.delegate){
6168                 t = e.getTarget(o.delegate, el);
6169                 if(!t){
6170                     return;
6171                 }
6172             }else{
6173                 t = e.target;
6174             }
6175             if(o.stopEvent === true){
6176                 e.stopEvent();
6177             }
6178             if(o.preventDefault === true){
6179                e.preventDefault();
6180             }
6181             if(o.stopPropagation === true){
6182                 e.stopPropagation();
6183             }
6184
6185             if(o.normalized === false){
6186                 e = e.browserEvent;
6187             }
6188
6189             fn.call(scope || el, e, t, o);
6190         };
6191         if(o.delay){
6192             h = createDelayed(h, o);
6193         }
6194         if(o.single){
6195             h = createSingle(h, el, ename, fn);
6196         }
6197         if(o.buffer){
6198             h = createBuffered(h, o);
6199         }
6200         fn._handlers = fn._handlers || [];
6201         
6202         
6203         fn._handlers.push([Roo.id(el), ename, h]);
6204         
6205         
6206          
6207         E.on(el, ename, h);
6208         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6209             el.addEventListener("DOMMouseScroll", h, false);
6210             E.on(window, 'unload', function(){
6211                 el.removeEventListener("DOMMouseScroll", h, false);
6212             });
6213         }
6214         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6215             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6216         }
6217         return h;
6218     };
6219
6220     var stopListening = function(el, ename, fn){
6221         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6222         if(hds){
6223             for(var i = 0, len = hds.length; i < len; i++){
6224                 var h = hds[i];
6225                 if(h[0] == id && h[1] == ename){
6226                     hd = h[2];
6227                     hds.splice(i, 1);
6228                     break;
6229                 }
6230             }
6231         }
6232         E.un(el, ename, hd);
6233         el = Roo.getDom(el);
6234         if(ename == "mousewheel" && el.addEventListener){
6235             el.removeEventListener("DOMMouseScroll", hd, false);
6236         }
6237         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6238             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6239         }
6240     };
6241
6242     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6243     
6244     var pub = {
6245         
6246         
6247         /** 
6248          * Fix for doc tools
6249          * @scope Roo.EventManager
6250          */
6251         
6252         
6253         /** 
6254          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6255          * object with a Roo.EventObject
6256          * @param {Function} fn        The method the event invokes
6257          * @param {Object}   scope    An object that becomes the scope of the handler
6258          * @param {boolean}  override If true, the obj passed in becomes
6259          *                             the execution scope of the listener
6260          * @return {Function} The wrapped function
6261          * @deprecated
6262          */
6263         wrap : function(fn, scope, override){
6264             return function(e){
6265                 Roo.EventObject.setEvent(e);
6266                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6267             };
6268         },
6269         
6270         /**
6271      * Appends an event handler to an element (shorthand for addListener)
6272      * @param {String/HTMLElement}   element        The html element or id to assign the
6273      * @param {String}   eventName The type of event to listen for
6274      * @param {Function} handler The method the event invokes
6275      * @param {Object}   scope (optional) The scope in which to execute the handler
6276      * function. The handler function's "this" context.
6277      * @param {Object}   options (optional) An object containing handler configuration
6278      * properties. This may contain any of the following properties:<ul>
6279      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6280      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6281      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6282      * <li>preventDefault {Boolean} True to prevent the default action</li>
6283      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6284      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6285      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6286      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6287      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6288      * by the specified number of milliseconds. If the event fires again within that time, the original
6289      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6290      * </ul><br>
6291      * <p>
6292      * <b>Combining Options</b><br>
6293      * Using the options argument, it is possible to combine different types of listeners:<br>
6294      * <br>
6295      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6296      * Code:<pre><code>
6297 el.on('click', this.onClick, this, {
6298     single: true,
6299     delay: 100,
6300     stopEvent : true,
6301     forumId: 4
6302 });</code></pre>
6303      * <p>
6304      * <b>Attaching multiple handlers in 1 call</b><br>
6305       * The method also allows for a single argument to be passed which is a config object containing properties
6306      * which specify multiple handlers.
6307      * <p>
6308      * Code:<pre><code>
6309 el.on({
6310     'click' : {
6311         fn: this.onClick
6312         scope: this,
6313         delay: 100
6314     },
6315     'mouseover' : {
6316         fn: this.onMouseOver
6317         scope: this
6318     },
6319     'mouseout' : {
6320         fn: this.onMouseOut
6321         scope: this
6322     }
6323 });</code></pre>
6324      * <p>
6325      * Or a shorthand syntax:<br>
6326      * Code:<pre><code>
6327 el.on({
6328     'click' : this.onClick,
6329     'mouseover' : this.onMouseOver,
6330     'mouseout' : this.onMouseOut
6331     scope: this
6332 });</code></pre>
6333      */
6334         addListener : function(element, eventName, fn, scope, options){
6335             if(typeof eventName == "object"){
6336                 var o = eventName;
6337                 for(var e in o){
6338                     if(propRe.test(e)){
6339                         continue;
6340                     }
6341                     if(typeof o[e] == "function"){
6342                         // shared options
6343                         listen(element, e, o, o[e], o.scope);
6344                     }else{
6345                         // individual options
6346                         listen(element, e, o[e]);
6347                     }
6348                 }
6349                 return;
6350             }
6351             return listen(element, eventName, options, fn, scope);
6352         },
6353         
6354         /**
6355          * Removes an event handler
6356          *
6357          * @param {String/HTMLElement}   element        The id or html element to remove the 
6358          *                             event from
6359          * @param {String}   eventName     The type of event
6360          * @param {Function} fn
6361          * @return {Boolean} True if a listener was actually removed
6362          */
6363         removeListener : function(element, eventName, fn){
6364             return stopListening(element, eventName, fn);
6365         },
6366         
6367         /**
6368          * Fires when the document is ready (before onload and before images are loaded). Can be 
6369          * accessed shorthanded Roo.onReady().
6370          * @param {Function} fn        The method the event invokes
6371          * @param {Object}   scope    An  object that becomes the scope of the handler
6372          * @param {boolean}  options
6373          */
6374         onDocumentReady : function(fn, scope, options){
6375             if(docReadyState){ // if it already fired
6376                 docReadyEvent.addListener(fn, scope, options);
6377                 docReadyEvent.fire();
6378                 docReadyEvent.clearListeners();
6379                 return;
6380             }
6381             if(!docReadyEvent){
6382                 initDocReady();
6383             }
6384             docReadyEvent.addListener(fn, scope, options);
6385         },
6386         
6387         /**
6388          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6389          * @param {Function} fn        The method the event invokes
6390          * @param {Object}   scope    An object that becomes the scope of the handler
6391          * @param {boolean}  options
6392          */
6393         onWindowResize : function(fn, scope, options){
6394             if(!resizeEvent){
6395                 resizeEvent = new Roo.util.Event();
6396                 resizeTask = new Roo.util.DelayedTask(function(){
6397                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6398                 });
6399                 E.on(window, "resize", function(){
6400                     if(Roo.isIE){
6401                         resizeTask.delay(50);
6402                     }else{
6403                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6404                     }
6405                 });
6406             }
6407             resizeEvent.addListener(fn, scope, options);
6408         },
6409
6410         /**
6411          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6412          * @param {Function} fn        The method the event invokes
6413          * @param {Object}   scope    An object that becomes the scope of the handler
6414          * @param {boolean}  options
6415          */
6416         onTextResize : function(fn, scope, options){
6417             if(!textEvent){
6418                 textEvent = new Roo.util.Event();
6419                 var textEl = new Roo.Element(document.createElement('div'));
6420                 textEl.dom.className = 'x-text-resize';
6421                 textEl.dom.innerHTML = 'X';
6422                 textEl.appendTo(document.body);
6423                 textSize = textEl.dom.offsetHeight;
6424                 setInterval(function(){
6425                     if(textEl.dom.offsetHeight != textSize){
6426                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6427                     }
6428                 }, this.textResizeInterval);
6429             }
6430             textEvent.addListener(fn, scope, options);
6431         },
6432
6433         /**
6434          * Removes the passed window resize listener.
6435          * @param {Function} fn        The method the event invokes
6436          * @param {Object}   scope    The scope of handler
6437          */
6438         removeResizeListener : function(fn, scope){
6439             if(resizeEvent){
6440                 resizeEvent.removeListener(fn, scope);
6441             }
6442         },
6443
6444         // private
6445         fireResize : function(){
6446             if(resizeEvent){
6447                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6448             }   
6449         },
6450         /**
6451          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6452          */
6453         ieDeferSrc : false,
6454         /**
6455          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6456          */
6457         textResizeInterval : 50
6458     };
6459     
6460     /**
6461      * Fix for doc tools
6462      * @scopeAlias pub=Roo.EventManager
6463      */
6464     
6465      /**
6466      * Appends an event handler to an element (shorthand for addListener)
6467      * @param {String/HTMLElement}   element        The html element or id to assign the
6468      * @param {String}   eventName The type of event to listen for
6469      * @param {Function} handler The method the event invokes
6470      * @param {Object}   scope (optional) The scope in which to execute the handler
6471      * function. The handler function's "this" context.
6472      * @param {Object}   options (optional) An object containing handler configuration
6473      * properties. This may contain any of the following properties:<ul>
6474      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6475      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6476      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6477      * <li>preventDefault {Boolean} True to prevent the default action</li>
6478      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6479      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6480      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6481      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6482      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6483      * by the specified number of milliseconds. If the event fires again within that time, the original
6484      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6485      * </ul><br>
6486      * <p>
6487      * <b>Combining Options</b><br>
6488      * Using the options argument, it is possible to combine different types of listeners:<br>
6489      * <br>
6490      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6491      * Code:<pre><code>
6492 el.on('click', this.onClick, this, {
6493     single: true,
6494     delay: 100,
6495     stopEvent : true,
6496     forumId: 4
6497 });</code></pre>
6498      * <p>
6499      * <b>Attaching multiple handlers in 1 call</b><br>
6500       * The method also allows for a single argument to be passed which is a config object containing properties
6501      * which specify multiple handlers.
6502      * <p>
6503      * Code:<pre><code>
6504 el.on({
6505     'click' : {
6506         fn: this.onClick
6507         scope: this,
6508         delay: 100
6509     },
6510     'mouseover' : {
6511         fn: this.onMouseOver
6512         scope: this
6513     },
6514     'mouseout' : {
6515         fn: this.onMouseOut
6516         scope: this
6517     }
6518 });</code></pre>
6519      * <p>
6520      * Or a shorthand syntax:<br>
6521      * Code:<pre><code>
6522 el.on({
6523     'click' : this.onClick,
6524     'mouseover' : this.onMouseOver,
6525     'mouseout' : this.onMouseOut
6526     scope: this
6527 });</code></pre>
6528      */
6529     pub.on = pub.addListener;
6530     pub.un = pub.removeListener;
6531
6532     pub.stoppedMouseDownEvent = new Roo.util.Event();
6533     return pub;
6534 }();
6535 /**
6536   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6537   * @param {Function} fn        The method the event invokes
6538   * @param {Object}   scope    An  object that becomes the scope of the handler
6539   * @param {boolean}  override If true, the obj passed in becomes
6540   *                             the execution scope of the listener
6541   * @member Roo
6542   * @method onReady
6543  */
6544 Roo.onReady = Roo.EventManager.onDocumentReady;
6545
6546 Roo.onReady(function(){
6547     var bd = Roo.get(document.body);
6548     if(!bd){ return; }
6549
6550     var cls = [
6551             Roo.isIE ? "roo-ie"
6552             : Roo.isGecko ? "roo-gecko"
6553             : Roo.isOpera ? "roo-opera"
6554             : Roo.isSafari ? "roo-safari" : ""];
6555
6556     if(Roo.isMac){
6557         cls.push("roo-mac");
6558     }
6559     if(Roo.isLinux){
6560         cls.push("roo-linux");
6561     }
6562     if(Roo.isBorderBox){
6563         cls.push('roo-border-box');
6564     }
6565     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6566         var p = bd.dom.parentNode;
6567         if(p){
6568             p.className += ' roo-strict';
6569         }
6570     }
6571     bd.addClass(cls.join(' '));
6572 });
6573
6574 /**
6575  * @class Roo.EventObject
6576  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6577  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6578  * Example:
6579  * <pre><code>
6580  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6581     e.preventDefault();
6582     var target = e.getTarget();
6583     ...
6584  }
6585  var myDiv = Roo.get("myDiv");
6586  myDiv.on("click", handleClick);
6587  //or
6588  Roo.EventManager.on("myDiv", 'click', handleClick);
6589  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6590  </code></pre>
6591  * @singleton
6592  */
6593 Roo.EventObject = function(){
6594     
6595     var E = Roo.lib.Event;
6596     
6597     // safari keypress events for special keys return bad keycodes
6598     var safariKeys = {
6599         63234 : 37, // left
6600         63235 : 39, // right
6601         63232 : 38, // up
6602         63233 : 40, // down
6603         63276 : 33, // page up
6604         63277 : 34, // page down
6605         63272 : 46, // delete
6606         63273 : 36, // home
6607         63275 : 35  // end
6608     };
6609
6610     // normalize button clicks
6611     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6612                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6613
6614     Roo.EventObjectImpl = function(e){
6615         if(e){
6616             this.setEvent(e.browserEvent || e);
6617         }
6618     };
6619     Roo.EventObjectImpl.prototype = {
6620         /**
6621          * Used to fix doc tools.
6622          * @scope Roo.EventObject.prototype
6623          */
6624             
6625
6626         
6627         
6628         /** The normal browser event */
6629         browserEvent : null,
6630         /** The button pressed in a mouse event */
6631         button : -1,
6632         /** True if the shift key was down during the event */
6633         shiftKey : false,
6634         /** True if the control key was down during the event */
6635         ctrlKey : false,
6636         /** True if the alt key was down during the event */
6637         altKey : false,
6638
6639         /** Key constant 
6640         * @type Number */
6641         BACKSPACE : 8,
6642         /** Key constant 
6643         * @type Number */
6644         TAB : 9,
6645         /** Key constant 
6646         * @type Number */
6647         RETURN : 13,
6648         /** Key constant 
6649         * @type Number */
6650         ENTER : 13,
6651         /** Key constant 
6652         * @type Number */
6653         SHIFT : 16,
6654         /** Key constant 
6655         * @type Number */
6656         CONTROL : 17,
6657         /** Key constant 
6658         * @type Number */
6659         ESC : 27,
6660         /** Key constant 
6661         * @type Number */
6662         SPACE : 32,
6663         /** Key constant 
6664         * @type Number */
6665         PAGEUP : 33,
6666         /** Key constant 
6667         * @type Number */
6668         PAGEDOWN : 34,
6669         /** Key constant 
6670         * @type Number */
6671         END : 35,
6672         /** Key constant 
6673         * @type Number */
6674         HOME : 36,
6675         /** Key constant 
6676         * @type Number */
6677         LEFT : 37,
6678         /** Key constant 
6679         * @type Number */
6680         UP : 38,
6681         /** Key constant 
6682         * @type Number */
6683         RIGHT : 39,
6684         /** Key constant 
6685         * @type Number */
6686         DOWN : 40,
6687         /** Key constant 
6688         * @type Number */
6689         DELETE : 46,
6690         /** Key constant 
6691         * @type Number */
6692         F5 : 116,
6693
6694            /** @private */
6695         setEvent : function(e){
6696             if(e == this || (e && e.browserEvent)){ // already wrapped
6697                 return e;
6698             }
6699             this.browserEvent = e;
6700             if(e){
6701                 // normalize buttons
6702                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6703                 if(e.type == 'click' && this.button == -1){
6704                     this.button = 0;
6705                 }
6706                 this.type = e.type;
6707                 this.shiftKey = e.shiftKey;
6708                 // mac metaKey behaves like ctrlKey
6709                 this.ctrlKey = e.ctrlKey || e.metaKey;
6710                 this.altKey = e.altKey;
6711                 // in getKey these will be normalized for the mac
6712                 this.keyCode = e.keyCode;
6713                 // keyup warnings on firefox.
6714                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6715                 // cache the target for the delayed and or buffered events
6716                 this.target = E.getTarget(e);
6717                 // same for XY
6718                 this.xy = E.getXY(e);
6719             }else{
6720                 this.button = -1;
6721                 this.shiftKey = false;
6722                 this.ctrlKey = false;
6723                 this.altKey = false;
6724                 this.keyCode = 0;
6725                 this.charCode =0;
6726                 this.target = null;
6727                 this.xy = [0, 0];
6728             }
6729             return this;
6730         },
6731
6732         /**
6733          * Stop the event (preventDefault and stopPropagation)
6734          */
6735         stopEvent : function(){
6736             if(this.browserEvent){
6737                 if(this.browserEvent.type == 'mousedown'){
6738                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6739                 }
6740                 E.stopEvent(this.browserEvent);
6741             }
6742         },
6743
6744         /**
6745          * Prevents the browsers default handling of the event.
6746          */
6747         preventDefault : function(){
6748             if(this.browserEvent){
6749                 E.preventDefault(this.browserEvent);
6750             }
6751         },
6752
6753         /** @private */
6754         isNavKeyPress : function(){
6755             var k = this.keyCode;
6756             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6757             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6758         },
6759
6760         isSpecialKey : function(){
6761             var k = this.keyCode;
6762             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6763             (k == 16) || (k == 17) ||
6764             (k >= 18 && k <= 20) ||
6765             (k >= 33 && k <= 35) ||
6766             (k >= 36 && k <= 39) ||
6767             (k >= 44 && k <= 45);
6768         },
6769         /**
6770          * Cancels bubbling of the event.
6771          */
6772         stopPropagation : function(){
6773             if(this.browserEvent){
6774                 if(this.type == 'mousedown'){
6775                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6776                 }
6777                 E.stopPropagation(this.browserEvent);
6778             }
6779         },
6780
6781         /**
6782          * Gets the key code for the event.
6783          * @return {Number}
6784          */
6785         getCharCode : function(){
6786             return this.charCode || this.keyCode;
6787         },
6788
6789         /**
6790          * Returns a normalized keyCode for the event.
6791          * @return {Number} The key code
6792          */
6793         getKey : function(){
6794             var k = this.keyCode || this.charCode;
6795             return Roo.isSafari ? (safariKeys[k] || k) : k;
6796         },
6797
6798         /**
6799          * Gets the x coordinate of the event.
6800          * @return {Number}
6801          */
6802         getPageX : function(){
6803             return this.xy[0];
6804         },
6805
6806         /**
6807          * Gets the y coordinate of the event.
6808          * @return {Number}
6809          */
6810         getPageY : function(){
6811             return this.xy[1];
6812         },
6813
6814         /**
6815          * Gets the time of the event.
6816          * @return {Number}
6817          */
6818         getTime : function(){
6819             if(this.browserEvent){
6820                 return E.getTime(this.browserEvent);
6821             }
6822             return null;
6823         },
6824
6825         /**
6826          * Gets the page coordinates of the event.
6827          * @return {Array} The xy values like [x, y]
6828          */
6829         getXY : function(){
6830             return this.xy;
6831         },
6832
6833         /**
6834          * Gets the target for the event.
6835          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6836          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6837                 search as a number or element (defaults to 10 || document.body)
6838          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6839          * @return {HTMLelement}
6840          */
6841         getTarget : function(selector, maxDepth, returnEl){
6842             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6843         },
6844         /**
6845          * Gets the related target.
6846          * @return {HTMLElement}
6847          */
6848         getRelatedTarget : function(){
6849             if(this.browserEvent){
6850                 return E.getRelatedTarget(this.browserEvent);
6851             }
6852             return null;
6853         },
6854
6855         /**
6856          * Normalizes mouse wheel delta across browsers
6857          * @return {Number} The delta
6858          */
6859         getWheelDelta : function(){
6860             var e = this.browserEvent;
6861             var delta = 0;
6862             if(e.wheelDelta){ /* IE/Opera. */
6863                 delta = e.wheelDelta/120;
6864             }else if(e.detail){ /* Mozilla case. */
6865                 delta = -e.detail/3;
6866             }
6867             return delta;
6868         },
6869
6870         /**
6871          * Returns true if the control, meta, shift or alt key was pressed during this event.
6872          * @return {Boolean}
6873          */
6874         hasModifier : function(){
6875             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6876         },
6877
6878         /**
6879          * Returns true if the target of this event equals el or is a child of el
6880          * @param {String/HTMLElement/Element} el
6881          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6882          * @return {Boolean}
6883          */
6884         within : function(el, related){
6885             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6886             return t && Roo.fly(el).contains(t);
6887         },
6888
6889         getPoint : function(){
6890             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6891         }
6892     };
6893
6894     return new Roo.EventObjectImpl();
6895 }();
6896             
6897     /*
6898  * Based on:
6899  * Ext JS Library 1.1.1
6900  * Copyright(c) 2006-2007, Ext JS, LLC.
6901  *
6902  * Originally Released Under LGPL - original licence link has changed is not relivant.
6903  *
6904  * Fork - LGPL
6905  * <script type="text/javascript">
6906  */
6907
6908  
6909 // was in Composite Element!??!?!
6910  
6911 (function(){
6912     var D = Roo.lib.Dom;
6913     var E = Roo.lib.Event;
6914     var A = Roo.lib.Anim;
6915
6916     // local style camelizing for speed
6917     var propCache = {};
6918     var camelRe = /(-[a-z])/gi;
6919     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6920     var view = document.defaultView;
6921
6922 /**
6923  * @class Roo.Element
6924  * Represents an Element in the DOM.<br><br>
6925  * Usage:<br>
6926 <pre><code>
6927 var el = Roo.get("my-div");
6928
6929 // or with getEl
6930 var el = getEl("my-div");
6931
6932 // or with a DOM element
6933 var el = Roo.get(myDivElement);
6934 </code></pre>
6935  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6936  * each call instead of constructing a new one.<br><br>
6937  * <b>Animations</b><br />
6938  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6939  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6940 <pre>
6941 Option    Default   Description
6942 --------- --------  ---------------------------------------------
6943 duration  .35       The duration of the animation in seconds
6944 easing    easeOut   The YUI easing method
6945 callback  none      A function to execute when the anim completes
6946 scope     this      The scope (this) of the callback function
6947 </pre>
6948 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6949 * manipulate the animation. Here's an example:
6950 <pre><code>
6951 var el = Roo.get("my-div");
6952
6953 // no animation
6954 el.setWidth(100);
6955
6956 // default animation
6957 el.setWidth(100, true);
6958
6959 // animation with some options set
6960 el.setWidth(100, {
6961     duration: 1,
6962     callback: this.foo,
6963     scope: this
6964 });
6965
6966 // using the "anim" property to get the Anim object
6967 var opt = {
6968     duration: 1,
6969     callback: this.foo,
6970     scope: this
6971 };
6972 el.setWidth(100, opt);
6973 ...
6974 if(opt.anim.isAnimated()){
6975     opt.anim.stop();
6976 }
6977 </code></pre>
6978 * <b> Composite (Collections of) Elements</b><br />
6979  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6980  * @constructor Create a new Element directly.
6981  * @param {String/HTMLElement} element
6982  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6983  */
6984     Roo.Element = function(element, forceNew){
6985         var dom = typeof element == "string" ?
6986                 document.getElementById(element) : element;
6987         if(!dom){ // invalid id/element
6988             return null;
6989         }
6990         var id = dom.id;
6991         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6992             return Roo.Element.cache[id];
6993         }
6994
6995         /**
6996          * The DOM element
6997          * @type HTMLElement
6998          */
6999         this.dom = dom;
7000
7001         /**
7002          * The DOM element ID
7003          * @type String
7004          */
7005         this.id = id || Roo.id(dom);
7006     };
7007
7008     var El = Roo.Element;
7009
7010     El.prototype = {
7011         /**
7012          * The element's default display mode  (defaults to "")
7013          * @type String
7014          */
7015         originalDisplay : "",
7016
7017         visibilityMode : 1,
7018         /**
7019          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7020          * @type String
7021          */
7022         defaultUnit : "px",
7023         /**
7024          * Sets the element's visibility mode. When setVisible() is called it
7025          * will use this to determine whether to set the visibility or the display property.
7026          * @param visMode Element.VISIBILITY or Element.DISPLAY
7027          * @return {Roo.Element} this
7028          */
7029         setVisibilityMode : function(visMode){
7030             this.visibilityMode = visMode;
7031             return this;
7032         },
7033         /**
7034          * Convenience method for setVisibilityMode(Element.DISPLAY)
7035          * @param {String} display (optional) What to set display to when visible
7036          * @return {Roo.Element} this
7037          */
7038         enableDisplayMode : function(display){
7039             this.setVisibilityMode(El.DISPLAY);
7040             if(typeof display != "undefined") this.originalDisplay = display;
7041             return this;
7042         },
7043
7044         /**
7045          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7046          * @param {String} selector The simple selector to test
7047          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7048                 search as a number or element (defaults to 10 || document.body)
7049          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7050          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7051          */
7052         findParent : function(simpleSelector, maxDepth, returnEl){
7053             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7054             maxDepth = maxDepth || 50;
7055             if(typeof maxDepth != "number"){
7056                 stopEl = Roo.getDom(maxDepth);
7057                 maxDepth = 10;
7058             }
7059             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7060                 if(dq.is(p, simpleSelector)){
7061                     return returnEl ? Roo.get(p) : p;
7062                 }
7063                 depth++;
7064                 p = p.parentNode;
7065             }
7066             return null;
7067         },
7068
7069
7070         /**
7071          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7072          * @param {String} selector The simple selector to test
7073          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7074                 search as a number or element (defaults to 10 || document.body)
7075          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7076          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7077          */
7078         findParentNode : function(simpleSelector, maxDepth, returnEl){
7079             var p = Roo.fly(this.dom.parentNode, '_internal');
7080             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7081         },
7082
7083         /**
7084          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7085          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7086          * @param {String} selector The simple selector to test
7087          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7088                 search as a number or element (defaults to 10 || document.body)
7089          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7090          */
7091         up : function(simpleSelector, maxDepth){
7092             return this.findParentNode(simpleSelector, maxDepth, true);
7093         },
7094
7095
7096
7097         /**
7098          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7099          * @param {String} selector The simple selector to test
7100          * @return {Boolean} True if this element matches the selector, else false
7101          */
7102         is : function(simpleSelector){
7103             return Roo.DomQuery.is(this.dom, simpleSelector);
7104         },
7105
7106         /**
7107          * Perform animation on this element.
7108          * @param {Object} args The YUI animation control args
7109          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7110          * @param {Function} onComplete (optional) Function to call when animation completes
7111          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7112          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7113          * @return {Roo.Element} this
7114          */
7115         animate : function(args, duration, onComplete, easing, animType){
7116             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7117             return this;
7118         },
7119
7120         /*
7121          * @private Internal animation call
7122          */
7123         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7124             animType = animType || 'run';
7125             opt = opt || {};
7126             var anim = Roo.lib.Anim[animType](
7127                 this.dom, args,
7128                 (opt.duration || defaultDur) || .35,
7129                 (opt.easing || defaultEase) || 'easeOut',
7130                 function(){
7131                     Roo.callback(cb, this);
7132                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7133                 },
7134                 this
7135             );
7136             opt.anim = anim;
7137             return anim;
7138         },
7139
7140         // private legacy anim prep
7141         preanim : function(a, i){
7142             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7143         },
7144
7145         /**
7146          * Removes worthless text nodes
7147          * @param {Boolean} forceReclean (optional) By default the element
7148          * keeps track if it has been cleaned already so
7149          * you can call this over and over. However, if you update the element and
7150          * need to force a reclean, you can pass true.
7151          */
7152         clean : function(forceReclean){
7153             if(this.isCleaned && forceReclean !== true){
7154                 return this;
7155             }
7156             var ns = /\S/;
7157             var d = this.dom, n = d.firstChild, ni = -1;
7158             while(n){
7159                 var nx = n.nextSibling;
7160                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7161                     d.removeChild(n);
7162                 }else{
7163                     n.nodeIndex = ++ni;
7164                 }
7165                 n = nx;
7166             }
7167             this.isCleaned = true;
7168             return this;
7169         },
7170
7171         // private
7172         calcOffsetsTo : function(el){
7173             el = Roo.get(el);
7174             var d = el.dom;
7175             var restorePos = false;
7176             if(el.getStyle('position') == 'static'){
7177                 el.position('relative');
7178                 restorePos = true;
7179             }
7180             var x = 0, y =0;
7181             var op = this.dom;
7182             while(op && op != d && op.tagName != 'HTML'){
7183                 x+= op.offsetLeft;
7184                 y+= op.offsetTop;
7185                 op = op.offsetParent;
7186             }
7187             if(restorePos){
7188                 el.position('static');
7189             }
7190             return [x, y];
7191         },
7192
7193         /**
7194          * Scrolls this element into view within the passed container.
7195          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7196          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7197          * @return {Roo.Element} this
7198          */
7199         scrollIntoView : function(container, hscroll){
7200             var c = Roo.getDom(container) || document.body;
7201             var el = this.dom;
7202
7203             var o = this.calcOffsetsTo(c),
7204                 l = o[0],
7205                 t = o[1],
7206                 b = t+el.offsetHeight,
7207                 r = l+el.offsetWidth;
7208
7209             var ch = c.clientHeight;
7210             var ct = parseInt(c.scrollTop, 10);
7211             var cl = parseInt(c.scrollLeft, 10);
7212             var cb = ct + ch;
7213             var cr = cl + c.clientWidth;
7214
7215             if(t < ct){
7216                 c.scrollTop = t;
7217             }else if(b > cb){
7218                 c.scrollTop = b-ch;
7219             }
7220
7221             if(hscroll !== false){
7222                 if(l < cl){
7223                     c.scrollLeft = l;
7224                 }else if(r > cr){
7225                     c.scrollLeft = r-c.clientWidth;
7226                 }
7227             }
7228             return this;
7229         },
7230
7231         // private
7232         scrollChildIntoView : function(child, hscroll){
7233             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7234         },
7235
7236         /**
7237          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7238          * the new height may not be available immediately.
7239          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7240          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7241          * @param {Function} onComplete (optional) Function to call when animation completes
7242          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7243          * @return {Roo.Element} this
7244          */
7245         autoHeight : function(animate, duration, onComplete, easing){
7246             var oldHeight = this.getHeight();
7247             this.clip();
7248             this.setHeight(1); // force clipping
7249             setTimeout(function(){
7250                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7251                 if(!animate){
7252                     this.setHeight(height);
7253                     this.unclip();
7254                     if(typeof onComplete == "function"){
7255                         onComplete();
7256                     }
7257                 }else{
7258                     this.setHeight(oldHeight); // restore original height
7259                     this.setHeight(height, animate, duration, function(){
7260                         this.unclip();
7261                         if(typeof onComplete == "function") onComplete();
7262                     }.createDelegate(this), easing);
7263                 }
7264             }.createDelegate(this), 0);
7265             return this;
7266         },
7267
7268         /**
7269          * Returns true if this element is an ancestor of the passed element
7270          * @param {HTMLElement/String} el The element to check
7271          * @return {Boolean} True if this element is an ancestor of el, else false
7272          */
7273         contains : function(el){
7274             if(!el){return false;}
7275             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7276         },
7277
7278         /**
7279          * Checks whether the element is currently visible using both visibility and display properties.
7280          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7281          * @return {Boolean} True if the element is currently visible, else false
7282          */
7283         isVisible : function(deep) {
7284             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7285             if(deep !== true || !vis){
7286                 return vis;
7287             }
7288             var p = this.dom.parentNode;
7289             while(p && p.tagName.toLowerCase() != "body"){
7290                 if(!Roo.fly(p, '_isVisible').isVisible()){
7291                     return false;
7292                 }
7293                 p = p.parentNode;
7294             }
7295             return true;
7296         },
7297
7298         /**
7299          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7300          * @param {String} selector The CSS selector
7301          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7302          * @return {CompositeElement/CompositeElementLite} The composite element
7303          */
7304         select : function(selector, unique){
7305             return El.select(selector, unique, this.dom);
7306         },
7307
7308         /**
7309          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7310          * @param {String} selector The CSS selector
7311          * @return {Array} An array of the matched nodes
7312          */
7313         query : function(selector, unique){
7314             return Roo.DomQuery.select(selector, this.dom);
7315         },
7316
7317         /**
7318          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7319          * @param {String} selector The CSS selector
7320          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7321          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7322          */
7323         child : function(selector, returnDom){
7324             var n = Roo.DomQuery.selectNode(selector, this.dom);
7325             return returnDom ? n : Roo.get(n);
7326         },
7327
7328         /**
7329          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7330          * @param {String} selector The CSS selector
7331          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7332          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7333          */
7334         down : function(selector, returnDom){
7335             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7336             return returnDom ? n : Roo.get(n);
7337         },
7338
7339         /**
7340          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7341          * @param {String} group The group the DD object is member of
7342          * @param {Object} config The DD config object
7343          * @param {Object} overrides An object containing methods to override/implement on the DD object
7344          * @return {Roo.dd.DD} The DD object
7345          */
7346         initDD : function(group, config, overrides){
7347             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7348             return Roo.apply(dd, overrides);
7349         },
7350
7351         /**
7352          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7353          * @param {String} group The group the DDProxy object is member of
7354          * @param {Object} config The DDProxy config object
7355          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7356          * @return {Roo.dd.DDProxy} The DDProxy object
7357          */
7358         initDDProxy : function(group, config, overrides){
7359             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7360             return Roo.apply(dd, overrides);
7361         },
7362
7363         /**
7364          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7365          * @param {String} group The group the DDTarget object is member of
7366          * @param {Object} config The DDTarget config object
7367          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7368          * @return {Roo.dd.DDTarget} The DDTarget object
7369          */
7370         initDDTarget : function(group, config, overrides){
7371             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7372             return Roo.apply(dd, overrides);
7373         },
7374
7375         /**
7376          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7377          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7378          * @param {Boolean} visible Whether the element is visible
7379          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7380          * @return {Roo.Element} this
7381          */
7382          setVisible : function(visible, animate){
7383             if(!animate || !A){
7384                 if(this.visibilityMode == El.DISPLAY){
7385                     this.setDisplayed(visible);
7386                 }else{
7387                     this.fixDisplay();
7388                     this.dom.style.visibility = visible ? "visible" : "hidden";
7389                 }
7390             }else{
7391                 // closure for composites
7392                 var dom = this.dom;
7393                 var visMode = this.visibilityMode;
7394                 if(visible){
7395                     this.setOpacity(.01);
7396                     this.setVisible(true);
7397                 }
7398                 this.anim({opacity: { to: (visible?1:0) }},
7399                       this.preanim(arguments, 1),
7400                       null, .35, 'easeIn', function(){
7401                          if(!visible){
7402                              if(visMode == El.DISPLAY){
7403                                  dom.style.display = "none";
7404                              }else{
7405                                  dom.style.visibility = "hidden";
7406                              }
7407                              Roo.get(dom).setOpacity(1);
7408                          }
7409                      });
7410             }
7411             return this;
7412         },
7413
7414         /**
7415          * Returns true if display is not "none"
7416          * @return {Boolean}
7417          */
7418         isDisplayed : function() {
7419             return this.getStyle("display") != "none";
7420         },
7421
7422         /**
7423          * Toggles the element's visibility or display, depending on visibility mode.
7424          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7425          * @return {Roo.Element} this
7426          */
7427         toggle : function(animate){
7428             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7429             return this;
7430         },
7431
7432         /**
7433          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7434          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7435          * @return {Roo.Element} this
7436          */
7437         setDisplayed : function(value) {
7438             if(typeof value == "boolean"){
7439                value = value ? this.originalDisplay : "none";
7440             }
7441             this.setStyle("display", value);
7442             return this;
7443         },
7444
7445         /**
7446          * Tries to focus the element. Any exceptions are caught and ignored.
7447          * @return {Roo.Element} this
7448          */
7449         focus : function() {
7450             try{
7451                 this.dom.focus();
7452             }catch(e){}
7453             return this;
7454         },
7455
7456         /**
7457          * Tries to blur the element. Any exceptions are caught and ignored.
7458          * @return {Roo.Element} this
7459          */
7460         blur : function() {
7461             try{
7462                 this.dom.blur();
7463             }catch(e){}
7464             return this;
7465         },
7466
7467         /**
7468          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7469          * @param {String/Array} className The CSS class to add, or an array of classes
7470          * @return {Roo.Element} this
7471          */
7472         addClass : function(className){
7473             if(className instanceof Array){
7474                 for(var i = 0, len = className.length; i < len; i++) {
7475                     this.addClass(className[i]);
7476                 }
7477             }else{
7478                 if(className && !this.hasClass(className)){
7479                     this.dom.className = this.dom.className + " " + className;
7480                 }
7481             }
7482             return this;
7483         },
7484
7485         /**
7486          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7487          * @param {String/Array} className The CSS class to add, or an array of classes
7488          * @return {Roo.Element} this
7489          */
7490         radioClass : function(className){
7491             var siblings = this.dom.parentNode.childNodes;
7492             for(var i = 0; i < siblings.length; i++) {
7493                 var s = siblings[i];
7494                 if(s.nodeType == 1){
7495                     Roo.get(s).removeClass(className);
7496                 }
7497             }
7498             this.addClass(className);
7499             return this;
7500         },
7501
7502         /**
7503          * Removes one or more CSS classes from the element.
7504          * @param {String/Array} className The CSS class to remove, or an array of classes
7505          * @return {Roo.Element} this
7506          */
7507         removeClass : function(className){
7508             if(!className || !this.dom.className){
7509                 return this;
7510             }
7511             if(className instanceof Array){
7512                 for(var i = 0, len = className.length; i < len; i++) {
7513                     this.removeClass(className[i]);
7514                 }
7515             }else{
7516                 if(this.hasClass(className)){
7517                     var re = this.classReCache[className];
7518                     if (!re) {
7519                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7520                        this.classReCache[className] = re;
7521                     }
7522                     this.dom.className =
7523                         this.dom.className.replace(re, " ");
7524                 }
7525             }
7526             return this;
7527         },
7528
7529         // private
7530         classReCache: {},
7531
7532         /**
7533          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7534          * @param {String} className The CSS class to toggle
7535          * @return {Roo.Element} this
7536          */
7537         toggleClass : function(className){
7538             if(this.hasClass(className)){
7539                 this.removeClass(className);
7540             }else{
7541                 this.addClass(className);
7542             }
7543             return this;
7544         },
7545
7546         /**
7547          * Checks if the specified CSS class exists on this element's DOM node.
7548          * @param {String} className The CSS class to check for
7549          * @return {Boolean} True if the class exists, else false
7550          */
7551         hasClass : function(className){
7552             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7553         },
7554
7555         /**
7556          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7557          * @param {String} oldClassName The CSS class to replace
7558          * @param {String} newClassName The replacement CSS class
7559          * @return {Roo.Element} this
7560          */
7561         replaceClass : function(oldClassName, newClassName){
7562             this.removeClass(oldClassName);
7563             this.addClass(newClassName);
7564             return this;
7565         },
7566
7567         /**
7568          * Returns an object with properties matching the styles requested.
7569          * For example, el.getStyles('color', 'font-size', 'width') might return
7570          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7571          * @param {String} style1 A style name
7572          * @param {String} style2 A style name
7573          * @param {String} etc.
7574          * @return {Object} The style object
7575          */
7576         getStyles : function(){
7577             var a = arguments, len = a.length, r = {};
7578             for(var i = 0; i < len; i++){
7579                 r[a[i]] = this.getStyle(a[i]);
7580             }
7581             return r;
7582         },
7583
7584         /**
7585          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7586          * @param {String} property The style property whose value is returned.
7587          * @return {String} The current value of the style property for this element.
7588          */
7589         getStyle : function(){
7590             return view && view.getComputedStyle ?
7591                 function(prop){
7592                     var el = this.dom, v, cs, camel;
7593                     if(prop == 'float'){
7594                         prop = "cssFloat";
7595                     }
7596                     if(el.style && (v = el.style[prop])){
7597                         return v;
7598                     }
7599                     if(cs = view.getComputedStyle(el, "")){
7600                         if(!(camel = propCache[prop])){
7601                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7602                         }
7603                         return cs[camel];
7604                     }
7605                     return null;
7606                 } :
7607                 function(prop){
7608                     var el = this.dom, v, cs, camel;
7609                     if(prop == 'opacity'){
7610                         if(typeof el.style.filter == 'string'){
7611                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7612                             if(m){
7613                                 var fv = parseFloat(m[1]);
7614                                 if(!isNaN(fv)){
7615                                     return fv ? fv / 100 : 0;
7616                                 }
7617                             }
7618                         }
7619                         return 1;
7620                     }else if(prop == 'float'){
7621                         prop = "styleFloat";
7622                     }
7623                     if(!(camel = propCache[prop])){
7624                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7625                     }
7626                     if(v = el.style[camel]){
7627                         return v;
7628                     }
7629                     if(cs = el.currentStyle){
7630                         return cs[camel];
7631                     }
7632                     return null;
7633                 };
7634         }(),
7635
7636         /**
7637          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7638          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7639          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7640          * @return {Roo.Element} this
7641          */
7642         setStyle : function(prop, value){
7643             if(typeof prop == "string"){
7644                 
7645                 if (prop == 'float') {
7646                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7647                     return this;
7648                 }
7649                 
7650                 var camel;
7651                 if(!(camel = propCache[prop])){
7652                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7653                 }
7654                 
7655                 if(camel == 'opacity') {
7656                     this.setOpacity(value);
7657                 }else{
7658                     this.dom.style[camel] = value;
7659                 }
7660             }else{
7661                 for(var style in prop){
7662                     if(typeof prop[style] != "function"){
7663                        this.setStyle(style, prop[style]);
7664                     }
7665                 }
7666             }
7667             return this;
7668         },
7669
7670         /**
7671          * More flexible version of {@link #setStyle} for setting style properties.
7672          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7673          * a function which returns such a specification.
7674          * @return {Roo.Element} this
7675          */
7676         applyStyles : function(style){
7677             Roo.DomHelper.applyStyles(this.dom, style);
7678             return this;
7679         },
7680
7681         /**
7682           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7683           * @return {Number} The X position of the element
7684           */
7685         getX : function(){
7686             return D.getX(this.dom);
7687         },
7688
7689         /**
7690           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7691           * @return {Number} The Y position of the element
7692           */
7693         getY : function(){
7694             return D.getY(this.dom);
7695         },
7696
7697         /**
7698           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7699           * @return {Array} The XY position of the element
7700           */
7701         getXY : function(){
7702             return D.getXY(this.dom);
7703         },
7704
7705         /**
7706          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7707          * @param {Number} The X position of the element
7708          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7709          * @return {Roo.Element} this
7710          */
7711         setX : function(x, animate){
7712             if(!animate || !A){
7713                 D.setX(this.dom, x);
7714             }else{
7715                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7716             }
7717             return this;
7718         },
7719
7720         /**
7721          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7722          * @param {Number} The Y position of the element
7723          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7724          * @return {Roo.Element} this
7725          */
7726         setY : function(y, animate){
7727             if(!animate || !A){
7728                 D.setY(this.dom, y);
7729             }else{
7730                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7731             }
7732             return this;
7733         },
7734
7735         /**
7736          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7737          * @param {String} left The left CSS property value
7738          * @return {Roo.Element} this
7739          */
7740         setLeft : function(left){
7741             this.setStyle("left", this.addUnits(left));
7742             return this;
7743         },
7744
7745         /**
7746          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7747          * @param {String} top The top CSS property value
7748          * @return {Roo.Element} this
7749          */
7750         setTop : function(top){
7751             this.setStyle("top", this.addUnits(top));
7752             return this;
7753         },
7754
7755         /**
7756          * Sets the element's CSS right style.
7757          * @param {String} right The right CSS property value
7758          * @return {Roo.Element} this
7759          */
7760         setRight : function(right){
7761             this.setStyle("right", this.addUnits(right));
7762             return this;
7763         },
7764
7765         /**
7766          * Sets the element's CSS bottom style.
7767          * @param {String} bottom The bottom CSS property value
7768          * @return {Roo.Element} this
7769          */
7770         setBottom : function(bottom){
7771             this.setStyle("bottom", this.addUnits(bottom));
7772             return this;
7773         },
7774
7775         /**
7776          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7777          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7778          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7779          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7780          * @return {Roo.Element} this
7781          */
7782         setXY : function(pos, animate){
7783             if(!animate || !A){
7784                 D.setXY(this.dom, pos);
7785             }else{
7786                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7787             }
7788             return this;
7789         },
7790
7791         /**
7792          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7793          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7794          * @param {Number} x X value for new position (coordinates are page-based)
7795          * @param {Number} y Y value for new position (coordinates are page-based)
7796          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7797          * @return {Roo.Element} this
7798          */
7799         setLocation : function(x, y, animate){
7800             this.setXY([x, y], this.preanim(arguments, 2));
7801             return this;
7802         },
7803
7804         /**
7805          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7806          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7807          * @param {Number} x X value for new position (coordinates are page-based)
7808          * @param {Number} y Y value for new position (coordinates are page-based)
7809          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7810          * @return {Roo.Element} this
7811          */
7812         moveTo : function(x, y, animate){
7813             this.setXY([x, y], this.preanim(arguments, 2));
7814             return this;
7815         },
7816
7817         /**
7818          * Returns the region of the given element.
7819          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7820          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7821          */
7822         getRegion : function(){
7823             return D.getRegion(this.dom);
7824         },
7825
7826         /**
7827          * Returns the offset height of the element
7828          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7829          * @return {Number} The element's height
7830          */
7831         getHeight : function(contentHeight){
7832             var h = this.dom.offsetHeight || 0;
7833             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7834         },
7835
7836         /**
7837          * Returns the offset width of the element
7838          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7839          * @return {Number} The element's width
7840          */
7841         getWidth : function(contentWidth){
7842             var w = this.dom.offsetWidth || 0;
7843             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7844         },
7845
7846         /**
7847          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7848          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7849          * if a height has not been set using CSS.
7850          * @return {Number}
7851          */
7852         getComputedHeight : function(){
7853             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7854             if(!h){
7855                 h = parseInt(this.getStyle('height'), 10) || 0;
7856                 if(!this.isBorderBox()){
7857                     h += this.getFrameWidth('tb');
7858                 }
7859             }
7860             return h;
7861         },
7862
7863         /**
7864          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7865          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7866          * if a width has not been set using CSS.
7867          * @return {Number}
7868          */
7869         getComputedWidth : function(){
7870             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7871             if(!w){
7872                 w = parseInt(this.getStyle('width'), 10) || 0;
7873                 if(!this.isBorderBox()){
7874                     w += this.getFrameWidth('lr');
7875                 }
7876             }
7877             return w;
7878         },
7879
7880         /**
7881          * Returns the size of the element.
7882          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7883          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7884          */
7885         getSize : function(contentSize){
7886             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7887         },
7888
7889         /**
7890          * Returns the width and height of the viewport.
7891          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7892          */
7893         getViewSize : function(){
7894             var d = this.dom, doc = document, aw = 0, ah = 0;
7895             if(d == doc || d == doc.body){
7896                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7897             }else{
7898                 return {
7899                     width : d.clientWidth,
7900                     height: d.clientHeight
7901                 };
7902             }
7903         },
7904
7905         /**
7906          * Returns the value of the "value" attribute
7907          * @param {Boolean} asNumber true to parse the value as a number
7908          * @return {String/Number}
7909          */
7910         getValue : function(asNumber){
7911             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7912         },
7913
7914         // private
7915         adjustWidth : function(width){
7916             if(typeof width == "number"){
7917                 if(this.autoBoxAdjust && !this.isBorderBox()){
7918                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7919                 }
7920                 if(width < 0){
7921                     width = 0;
7922                 }
7923             }
7924             return width;
7925         },
7926
7927         // private
7928         adjustHeight : function(height){
7929             if(typeof height == "number"){
7930                if(this.autoBoxAdjust && !this.isBorderBox()){
7931                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7932                }
7933                if(height < 0){
7934                    height = 0;
7935                }
7936             }
7937             return height;
7938         },
7939
7940         /**
7941          * Set the width of the element
7942          * @param {Number} width The new width
7943          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7944          * @return {Roo.Element} this
7945          */
7946         setWidth : function(width, animate){
7947             width = this.adjustWidth(width);
7948             if(!animate || !A){
7949                 this.dom.style.width = this.addUnits(width);
7950             }else{
7951                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7952             }
7953             return this;
7954         },
7955
7956         /**
7957          * Set the height of the element
7958          * @param {Number} height The new height
7959          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7960          * @return {Roo.Element} this
7961          */
7962          setHeight : function(height, animate){
7963             height = this.adjustHeight(height);
7964             if(!animate || !A){
7965                 this.dom.style.height = this.addUnits(height);
7966             }else{
7967                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7968             }
7969             return this;
7970         },
7971
7972         /**
7973          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7974          * @param {Number} width The new width
7975          * @param {Number} height The new height
7976          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7977          * @return {Roo.Element} this
7978          */
7979          setSize : function(width, height, animate){
7980             if(typeof width == "object"){ // in case of object from getSize()
7981                 height = width.height; width = width.width;
7982             }
7983             width = this.adjustWidth(width); height = this.adjustHeight(height);
7984             if(!animate || !A){
7985                 this.dom.style.width = this.addUnits(width);
7986                 this.dom.style.height = this.addUnits(height);
7987             }else{
7988                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7989             }
7990             return this;
7991         },
7992
7993         /**
7994          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7995          * @param {Number} x X value for new position (coordinates are page-based)
7996          * @param {Number} y Y value for new position (coordinates are page-based)
7997          * @param {Number} width The new width
7998          * @param {Number} height The new height
7999          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8000          * @return {Roo.Element} this
8001          */
8002         setBounds : function(x, y, width, height, animate){
8003             if(!animate || !A){
8004                 this.setSize(width, height);
8005                 this.setLocation(x, y);
8006             }else{
8007                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8008                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8009                               this.preanim(arguments, 4), 'motion');
8010             }
8011             return this;
8012         },
8013
8014         /**
8015          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
8016          * @param {Roo.lib.Region} region The region to fill
8017          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8018          * @return {Roo.Element} this
8019          */
8020         setRegion : function(region, animate){
8021             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8022             return this;
8023         },
8024
8025         /**
8026          * Appends an event handler
8027          *
8028          * @param {String}   eventName     The type of event to append
8029          * @param {Function} fn        The method the event invokes
8030          * @param {Object} scope       (optional) The scope (this object) of the fn
8031          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8032          */
8033         addListener : function(eventName, fn, scope, options){
8034             if (this.dom) {
8035                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8036             }
8037         },
8038
8039         /**
8040          * Removes an event handler from this element
8041          * @param {String} eventName the type of event to remove
8042          * @param {Function} fn the method the event invokes
8043          * @return {Roo.Element} this
8044          */
8045         removeListener : function(eventName, fn){
8046             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8047             return this;
8048         },
8049
8050         /**
8051          * Removes all previous added listeners from this element
8052          * @return {Roo.Element} this
8053          */
8054         removeAllListeners : function(){
8055             E.purgeElement(this.dom);
8056             return this;
8057         },
8058
8059         relayEvent : function(eventName, observable){
8060             this.on(eventName, function(e){
8061                 observable.fireEvent(eventName, e);
8062             });
8063         },
8064
8065         /**
8066          * Set the opacity of the element
8067          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8068          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8069          * @return {Roo.Element} this
8070          */
8071          setOpacity : function(opacity, animate){
8072             if(!animate || !A){
8073                 var s = this.dom.style;
8074                 if(Roo.isIE){
8075                     s.zoom = 1;
8076                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8077                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8078                 }else{
8079                     s.opacity = opacity;
8080                 }
8081             }else{
8082                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8083             }
8084             return this;
8085         },
8086
8087         /**
8088          * Gets the left X coordinate
8089          * @param {Boolean} local True to get the local css position instead of page coordinate
8090          * @return {Number}
8091          */
8092         getLeft : function(local){
8093             if(!local){
8094                 return this.getX();
8095             }else{
8096                 return parseInt(this.getStyle("left"), 10) || 0;
8097             }
8098         },
8099
8100         /**
8101          * Gets the right X coordinate of the element (element X position + element width)
8102          * @param {Boolean} local True to get the local css position instead of page coordinate
8103          * @return {Number}
8104          */
8105         getRight : function(local){
8106             if(!local){
8107                 return this.getX() + this.getWidth();
8108             }else{
8109                 return (this.getLeft(true) + this.getWidth()) || 0;
8110             }
8111         },
8112
8113         /**
8114          * Gets the top Y coordinate
8115          * @param {Boolean} local True to get the local css position instead of page coordinate
8116          * @return {Number}
8117          */
8118         getTop : function(local) {
8119             if(!local){
8120                 return this.getY();
8121             }else{
8122                 return parseInt(this.getStyle("top"), 10) || 0;
8123             }
8124         },
8125
8126         /**
8127          * Gets the bottom Y coordinate of the element (element Y position + element height)
8128          * @param {Boolean} local True to get the local css position instead of page coordinate
8129          * @return {Number}
8130          */
8131         getBottom : function(local){
8132             if(!local){
8133                 return this.getY() + this.getHeight();
8134             }else{
8135                 return (this.getTop(true) + this.getHeight()) || 0;
8136             }
8137         },
8138
8139         /**
8140         * Initializes positioning on this element. If a desired position is not passed, it will make the
8141         * the element positioned relative IF it is not already positioned.
8142         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8143         * @param {Number} zIndex (optional) The zIndex to apply
8144         * @param {Number} x (optional) Set the page X position
8145         * @param {Number} y (optional) Set the page Y position
8146         */
8147         position : function(pos, zIndex, x, y){
8148             if(!pos){
8149                if(this.getStyle('position') == 'static'){
8150                    this.setStyle('position', 'relative');
8151                }
8152             }else{
8153                 this.setStyle("position", pos);
8154             }
8155             if(zIndex){
8156                 this.setStyle("z-index", zIndex);
8157             }
8158             if(x !== undefined && y !== undefined){
8159                 this.setXY([x, y]);
8160             }else if(x !== undefined){
8161                 this.setX(x);
8162             }else if(y !== undefined){
8163                 this.setY(y);
8164             }
8165         },
8166
8167         /**
8168         * Clear positioning back to the default when the document was loaded
8169         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8170         * @return {Roo.Element} this
8171          */
8172         clearPositioning : function(value){
8173             value = value ||'';
8174             this.setStyle({
8175                 "left": value,
8176                 "right": value,
8177                 "top": value,
8178                 "bottom": value,
8179                 "z-index": "",
8180                 "position" : "static"
8181             });
8182             return this;
8183         },
8184
8185         /**
8186         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8187         * snapshot before performing an update and then restoring the element.
8188         * @return {Object}
8189         */
8190         getPositioning : function(){
8191             var l = this.getStyle("left");
8192             var t = this.getStyle("top");
8193             return {
8194                 "position" : this.getStyle("position"),
8195                 "left" : l,
8196                 "right" : l ? "" : this.getStyle("right"),
8197                 "top" : t,
8198                 "bottom" : t ? "" : this.getStyle("bottom"),
8199                 "z-index" : this.getStyle("z-index")
8200             };
8201         },
8202
8203         /**
8204          * Gets the width of the border(s) for the specified side(s)
8205          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8206          * passing lr would get the border (l)eft width + the border (r)ight width.
8207          * @return {Number} The width of the sides passed added together
8208          */
8209         getBorderWidth : function(side){
8210             return this.addStyles(side, El.borders);
8211         },
8212
8213         /**
8214          * Gets the width of the padding(s) for the specified side(s)
8215          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8216          * passing lr would get the padding (l)eft + the padding (r)ight.
8217          * @return {Number} The padding of the sides passed added together
8218          */
8219         getPadding : function(side){
8220             return this.addStyles(side, El.paddings);
8221         },
8222
8223         /**
8224         * Set positioning with an object returned by getPositioning().
8225         * @param {Object} posCfg
8226         * @return {Roo.Element} this
8227          */
8228         setPositioning : function(pc){
8229             this.applyStyles(pc);
8230             if(pc.right == "auto"){
8231                 this.dom.style.right = "";
8232             }
8233             if(pc.bottom == "auto"){
8234                 this.dom.style.bottom = "";
8235             }
8236             return this;
8237         },
8238
8239         // private
8240         fixDisplay : function(){
8241             if(this.getStyle("display") == "none"){
8242                 this.setStyle("visibility", "hidden");
8243                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8244                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8245                     this.setStyle("display", "block");
8246                 }
8247             }
8248         },
8249
8250         /**
8251          * Quick set left and top adding default units
8252          * @param {String} left The left CSS property value
8253          * @param {String} top The top CSS property value
8254          * @return {Roo.Element} this
8255          */
8256          setLeftTop : function(left, top){
8257             this.dom.style.left = this.addUnits(left);
8258             this.dom.style.top = this.addUnits(top);
8259             return this;
8260         },
8261
8262         /**
8263          * Move this element relative to its current position.
8264          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8265          * @param {Number} distance How far to move the element in pixels
8266          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8267          * @return {Roo.Element} this
8268          */
8269          move : function(direction, distance, animate){
8270             var xy = this.getXY();
8271             direction = direction.toLowerCase();
8272             switch(direction){
8273                 case "l":
8274                 case "left":
8275                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8276                     break;
8277                case "r":
8278                case "right":
8279                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8280                     break;
8281                case "t":
8282                case "top":
8283                case "up":
8284                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8285                     break;
8286                case "b":
8287                case "bottom":
8288                case "down":
8289                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8290                     break;
8291             }
8292             return this;
8293         },
8294
8295         /**
8296          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8297          * @return {Roo.Element} this
8298          */
8299         clip : function(){
8300             if(!this.isClipped){
8301                this.isClipped = true;
8302                this.originalClip = {
8303                    "o": this.getStyle("overflow"),
8304                    "x": this.getStyle("overflow-x"),
8305                    "y": this.getStyle("overflow-y")
8306                };
8307                this.setStyle("overflow", "hidden");
8308                this.setStyle("overflow-x", "hidden");
8309                this.setStyle("overflow-y", "hidden");
8310             }
8311             return this;
8312         },
8313
8314         /**
8315          *  Return clipping (overflow) to original clipping before clip() was called
8316          * @return {Roo.Element} this
8317          */
8318         unclip : function(){
8319             if(this.isClipped){
8320                 this.isClipped = false;
8321                 var o = this.originalClip;
8322                 if(o.o){this.setStyle("overflow", o.o);}
8323                 if(o.x){this.setStyle("overflow-x", o.x);}
8324                 if(o.y){this.setStyle("overflow-y", o.y);}
8325             }
8326             return this;
8327         },
8328
8329
8330         /**
8331          * Gets the x,y coordinates specified by the anchor position on the element.
8332          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8333          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8334          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8335          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8336          * @return {Array} [x, y] An array containing the element's x and y coordinates
8337          */
8338         getAnchorXY : function(anchor, local, s){
8339             //Passing a different size is useful for pre-calculating anchors,
8340             //especially for anchored animations that change the el size.
8341
8342             var w, h, vp = false;
8343             if(!s){
8344                 var d = this.dom;
8345                 if(d == document.body || d == document){
8346                     vp = true;
8347                     w = D.getViewWidth(); h = D.getViewHeight();
8348                 }else{
8349                     w = this.getWidth(); h = this.getHeight();
8350                 }
8351             }else{
8352                 w = s.width;  h = s.height;
8353             }
8354             var x = 0, y = 0, r = Math.round;
8355             switch((anchor || "tl").toLowerCase()){
8356                 case "c":
8357                     x = r(w*.5);
8358                     y = r(h*.5);
8359                 break;
8360                 case "t":
8361                     x = r(w*.5);
8362                     y = 0;
8363                 break;
8364                 case "l":
8365                     x = 0;
8366                     y = r(h*.5);
8367                 break;
8368                 case "r":
8369                     x = w;
8370                     y = r(h*.5);
8371                 break;
8372                 case "b":
8373                     x = r(w*.5);
8374                     y = h;
8375                 break;
8376                 case "tl":
8377                     x = 0;
8378                     y = 0;
8379                 break;
8380                 case "bl":
8381                     x = 0;
8382                     y = h;
8383                 break;
8384                 case "br":
8385                     x = w;
8386                     y = h;
8387                 break;
8388                 case "tr":
8389                     x = w;
8390                     y = 0;
8391                 break;
8392             }
8393             if(local === true){
8394                 return [x, y];
8395             }
8396             if(vp){
8397                 var sc = this.getScroll();
8398                 return [x + sc.left, y + sc.top];
8399             }
8400             //Add the element's offset xy
8401             var o = this.getXY();
8402             return [x+o[0], y+o[1]];
8403         },
8404
8405         /**
8406          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8407          * supported position values.
8408          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8409          * @param {String} position The position to align to.
8410          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8411          * @return {Array} [x, y]
8412          */
8413         getAlignToXY : function(el, p, o){
8414             el = Roo.get(el);
8415             var d = this.dom;
8416             if(!el.dom){
8417                 throw "Element.alignTo with an element that doesn't exist";
8418             }
8419             var c = false; //constrain to viewport
8420             var p1 = "", p2 = "";
8421             o = o || [0,0];
8422
8423             if(!p){
8424                 p = "tl-bl";
8425             }else if(p == "?"){
8426                 p = "tl-bl?";
8427             }else if(p.indexOf("-") == -1){
8428                 p = "tl-" + p;
8429             }
8430             p = p.toLowerCase();
8431             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8432             if(!m){
8433                throw "Element.alignTo with an invalid alignment " + p;
8434             }
8435             p1 = m[1]; p2 = m[2]; c = !!m[3];
8436
8437             //Subtract the aligned el's internal xy from the target's offset xy
8438             //plus custom offset to get the aligned el's new offset xy
8439             var a1 = this.getAnchorXY(p1, true);
8440             var a2 = el.getAnchorXY(p2, false);
8441             var x = a2[0] - a1[0] + o[0];
8442             var y = a2[1] - a1[1] + o[1];
8443             if(c){
8444                 //constrain the aligned el to viewport if necessary
8445                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8446                 // 5px of margin for ie
8447                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8448
8449                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8450                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8451                 //otherwise swap the aligned el to the opposite border of the target.
8452                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8453                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8454                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8455                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8456
8457                var doc = document;
8458                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8459                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8460
8461                if((x+w) > dw + scrollX){
8462                     x = swapX ? r.left-w : dw+scrollX-w;
8463                 }
8464                if(x < scrollX){
8465                    x = swapX ? r.right : scrollX;
8466                }
8467                if((y+h) > dh + scrollY){
8468                     y = swapY ? r.top-h : dh+scrollY-h;
8469                 }
8470                if (y < scrollY){
8471                    y = swapY ? r.bottom : scrollY;
8472                }
8473             }
8474             return [x,y];
8475         },
8476
8477         // private
8478         getConstrainToXY : function(){
8479             var os = {top:0, left:0, bottom:0, right: 0};
8480
8481             return function(el, local, offsets, proposedXY){
8482                 el = Roo.get(el);
8483                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8484
8485                 var vw, vh, vx = 0, vy = 0;
8486                 if(el.dom == document.body || el.dom == document){
8487                     vw = Roo.lib.Dom.getViewWidth();
8488                     vh = Roo.lib.Dom.getViewHeight();
8489                 }else{
8490                     vw = el.dom.clientWidth;
8491                     vh = el.dom.clientHeight;
8492                     if(!local){
8493                         var vxy = el.getXY();
8494                         vx = vxy[0];
8495                         vy = vxy[1];
8496                     }
8497                 }
8498
8499                 var s = el.getScroll();
8500
8501                 vx += offsets.left + s.left;
8502                 vy += offsets.top + s.top;
8503
8504                 vw -= offsets.right;
8505                 vh -= offsets.bottom;
8506
8507                 var vr = vx+vw;
8508                 var vb = vy+vh;
8509
8510                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8511                 var x = xy[0], y = xy[1];
8512                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8513
8514                 // only move it if it needs it
8515                 var moved = false;
8516
8517                 // first validate right/bottom
8518                 if((x + w) > vr){
8519                     x = vr - w;
8520                     moved = true;
8521                 }
8522                 if((y + h) > vb){
8523                     y = vb - h;
8524                     moved = true;
8525                 }
8526                 // then make sure top/left isn't negative
8527                 if(x < vx){
8528                     x = vx;
8529                     moved = true;
8530                 }
8531                 if(y < vy){
8532                     y = vy;
8533                     moved = true;
8534                 }
8535                 return moved ? [x, y] : false;
8536             };
8537         }(),
8538
8539         // private
8540         adjustForConstraints : function(xy, parent, offsets){
8541             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8542         },
8543
8544         /**
8545          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8546          * document it aligns it to the viewport.
8547          * The position parameter is optional, and can be specified in any one of the following formats:
8548          * <ul>
8549          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8550          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8551          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8552          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8553          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8554          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8555          * </ul>
8556          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8557          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8558          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8559          * that specified in order to enforce the viewport constraints.
8560          * Following are all of the supported anchor positions:
8561     <pre>
8562     Value  Description
8563     -----  -----------------------------
8564     tl     The top left corner (default)
8565     t      The center of the top edge
8566     tr     The top right corner
8567     l      The center of the left edge
8568     c      In the center of the element
8569     r      The center of the right edge
8570     bl     The bottom left corner
8571     b      The center of the bottom edge
8572     br     The bottom right corner
8573     </pre>
8574     Example Usage:
8575     <pre><code>
8576     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8577     el.alignTo("other-el");
8578
8579     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8580     el.alignTo("other-el", "tr?");
8581
8582     // align the bottom right corner of el with the center left edge of other-el
8583     el.alignTo("other-el", "br-l?");
8584
8585     // align the center of el with the bottom left corner of other-el and
8586     // adjust the x position by -6 pixels (and the y position by 0)
8587     el.alignTo("other-el", "c-bl", [-6, 0]);
8588     </code></pre>
8589          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8590          * @param {String} position The position to align to.
8591          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8592          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8593          * @return {Roo.Element} this
8594          */
8595         alignTo : function(element, position, offsets, animate){
8596             var xy = this.getAlignToXY(element, position, offsets);
8597             this.setXY(xy, this.preanim(arguments, 3));
8598             return this;
8599         },
8600
8601         /**
8602          * Anchors an element to another element and realigns it when the window is resized.
8603          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8604          * @param {String} position The position to align to.
8605          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8606          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8607          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8608          * is a number, it is used as the buffer delay (defaults to 50ms).
8609          * @param {Function} callback The function to call after the animation finishes
8610          * @return {Roo.Element} this
8611          */
8612         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8613             var action = function(){
8614                 this.alignTo(el, alignment, offsets, animate);
8615                 Roo.callback(callback, this);
8616             };
8617             Roo.EventManager.onWindowResize(action, this);
8618             var tm = typeof monitorScroll;
8619             if(tm != 'undefined'){
8620                 Roo.EventManager.on(window, 'scroll', action, this,
8621                     {buffer: tm == 'number' ? monitorScroll : 50});
8622             }
8623             action.call(this); // align immediately
8624             return this;
8625         },
8626         /**
8627          * Clears any opacity settings from this element. Required in some cases for IE.
8628          * @return {Roo.Element} this
8629          */
8630         clearOpacity : function(){
8631             if (window.ActiveXObject) {
8632                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8633                     this.dom.style.filter = "";
8634                 }
8635             } else {
8636                 this.dom.style.opacity = "";
8637                 this.dom.style["-moz-opacity"] = "";
8638                 this.dom.style["-khtml-opacity"] = "";
8639             }
8640             return this;
8641         },
8642
8643         /**
8644          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8645          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8646          * @return {Roo.Element} this
8647          */
8648         hide : function(animate){
8649             this.setVisible(false, this.preanim(arguments, 0));
8650             return this;
8651         },
8652
8653         /**
8654         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8655         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8656          * @return {Roo.Element} this
8657          */
8658         show : function(animate){
8659             this.setVisible(true, this.preanim(arguments, 0));
8660             return this;
8661         },
8662
8663         /**
8664          * @private Test if size has a unit, otherwise appends the default
8665          */
8666         addUnits : function(size){
8667             return Roo.Element.addUnits(size, this.defaultUnit);
8668         },
8669
8670         /**
8671          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8672          * @return {Roo.Element} this
8673          */
8674         beginMeasure : function(){
8675             var el = this.dom;
8676             if(el.offsetWidth || el.offsetHeight){
8677                 return this; // offsets work already
8678             }
8679             var changed = [];
8680             var p = this.dom, b = document.body; // start with this element
8681             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8682                 var pe = Roo.get(p);
8683                 if(pe.getStyle('display') == 'none'){
8684                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8685                     p.style.visibility = "hidden";
8686                     p.style.display = "block";
8687                 }
8688                 p = p.parentNode;
8689             }
8690             this._measureChanged = changed;
8691             return this;
8692
8693         },
8694
8695         /**
8696          * Restores displays to before beginMeasure was called
8697          * @return {Roo.Element} this
8698          */
8699         endMeasure : function(){
8700             var changed = this._measureChanged;
8701             if(changed){
8702                 for(var i = 0, len = changed.length; i < len; i++) {
8703                     var r = changed[i];
8704                     r.el.style.visibility = r.visibility;
8705                     r.el.style.display = "none";
8706                 }
8707                 this._measureChanged = null;
8708             }
8709             return this;
8710         },
8711
8712         /**
8713         * Update the innerHTML of this element, optionally searching for and processing scripts
8714         * @param {String} html The new HTML
8715         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8716         * @param {Function} callback For async script loading you can be noticed when the update completes
8717         * @return {Roo.Element} this
8718          */
8719         update : function(html, loadScripts, callback){
8720             if(typeof html == "undefined"){
8721                 html = "";
8722             }
8723             if(loadScripts !== true){
8724                 this.dom.innerHTML = html;
8725                 if(typeof callback == "function"){
8726                     callback();
8727                 }
8728                 return this;
8729             }
8730             var id = Roo.id();
8731             var dom = this.dom;
8732
8733             html += '<span id="' + id + '"></span>';
8734
8735             E.onAvailable(id, function(){
8736                 var hd = document.getElementsByTagName("head")[0];
8737                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8738                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8739                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8740
8741                 var match;
8742                 while(match = re.exec(html)){
8743                     var attrs = match[1];
8744                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8745                     if(srcMatch && srcMatch[2]){
8746                        var s = document.createElement("script");
8747                        s.src = srcMatch[2];
8748                        var typeMatch = attrs.match(typeRe);
8749                        if(typeMatch && typeMatch[2]){
8750                            s.type = typeMatch[2];
8751                        }
8752                        hd.appendChild(s);
8753                     }else if(match[2] && match[2].length > 0){
8754                         if(window.execScript) {
8755                            window.execScript(match[2]);
8756                         } else {
8757                             /**
8758                              * eval:var:id
8759                              * eval:var:dom
8760                              * eval:var:html
8761                              * 
8762                              */
8763                            window.eval(match[2]);
8764                         }
8765                     }
8766                 }
8767                 var el = document.getElementById(id);
8768                 if(el){el.parentNode.removeChild(el);}
8769                 if(typeof callback == "function"){
8770                     callback();
8771                 }
8772             });
8773             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8774             return this;
8775         },
8776
8777         /**
8778          * Direct access to the UpdateManager update() method (takes the same parameters).
8779          * @param {String/Function} url The url for this request or a function to call to get the url
8780          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8781          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8782          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8783          * @return {Roo.Element} this
8784          */
8785         load : function(){
8786             var um = this.getUpdateManager();
8787             um.update.apply(um, arguments);
8788             return this;
8789         },
8790
8791         /**
8792         * Gets this element's UpdateManager
8793         * @return {Roo.UpdateManager} The UpdateManager
8794         */
8795         getUpdateManager : function(){
8796             if(!this.updateManager){
8797                 this.updateManager = new Roo.UpdateManager(this);
8798             }
8799             return this.updateManager;
8800         },
8801
8802         /**
8803          * Disables text selection for this element (normalized across browsers)
8804          * @return {Roo.Element} this
8805          */
8806         unselectable : function(){
8807             this.dom.unselectable = "on";
8808             this.swallowEvent("selectstart", true);
8809             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8810             this.addClass("x-unselectable");
8811             return this;
8812         },
8813
8814         /**
8815         * Calculates the x, y to center this element on the screen
8816         * @return {Array} The x, y values [x, y]
8817         */
8818         getCenterXY : function(){
8819             return this.getAlignToXY(document, 'c-c');
8820         },
8821
8822         /**
8823         * Centers the Element in either the viewport, or another Element.
8824         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8825         */
8826         center : function(centerIn){
8827             this.alignTo(centerIn || document, 'c-c');
8828             return this;
8829         },
8830
8831         /**
8832          * Tests various css rules/browsers to determine if this element uses a border box
8833          * @return {Boolean}
8834          */
8835         isBorderBox : function(){
8836             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8837         },
8838
8839         /**
8840          * Return a box {x, y, width, height} that can be used to set another elements
8841          * size/location to match this element.
8842          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8843          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8844          * @return {Object} box An object in the format {x, y, width, height}
8845          */
8846         getBox : function(contentBox, local){
8847             var xy;
8848             if(!local){
8849                 xy = this.getXY();
8850             }else{
8851                 var left = parseInt(this.getStyle("left"), 10) || 0;
8852                 var top = parseInt(this.getStyle("top"), 10) || 0;
8853                 xy = [left, top];
8854             }
8855             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8856             if(!contentBox){
8857                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8858             }else{
8859                 var l = this.getBorderWidth("l")+this.getPadding("l");
8860                 var r = this.getBorderWidth("r")+this.getPadding("r");
8861                 var t = this.getBorderWidth("t")+this.getPadding("t");
8862                 var b = this.getBorderWidth("b")+this.getPadding("b");
8863                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8864             }
8865             bx.right = bx.x + bx.width;
8866             bx.bottom = bx.y + bx.height;
8867             return bx;
8868         },
8869
8870         /**
8871          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8872          for more information about the sides.
8873          * @param {String} sides
8874          * @return {Number}
8875          */
8876         getFrameWidth : function(sides, onlyContentBox){
8877             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8878         },
8879
8880         /**
8881          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8882          * @param {Object} box The box to fill {x, y, width, height}
8883          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8884          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8885          * @return {Roo.Element} this
8886          */
8887         setBox : function(box, adjust, animate){
8888             var w = box.width, h = box.height;
8889             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8890                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8891                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8892             }
8893             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8894             return this;
8895         },
8896
8897         /**
8898          * Forces the browser to repaint this element
8899          * @return {Roo.Element} this
8900          */
8901          repaint : function(){
8902             var dom = this.dom;
8903             this.addClass("x-repaint");
8904             setTimeout(function(){
8905                 Roo.get(dom).removeClass("x-repaint");
8906             }, 1);
8907             return this;
8908         },
8909
8910         /**
8911          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8912          * then it returns the calculated width of the sides (see getPadding)
8913          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8914          * @return {Object/Number}
8915          */
8916         getMargins : function(side){
8917             if(!side){
8918                 return {
8919                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8920                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8921                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8922                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8923                 };
8924             }else{
8925                 return this.addStyles(side, El.margins);
8926              }
8927         },
8928
8929         // private
8930         addStyles : function(sides, styles){
8931             var val = 0, v, w;
8932             for(var i = 0, len = sides.length; i < len; i++){
8933                 v = this.getStyle(styles[sides.charAt(i)]);
8934                 if(v){
8935                      w = parseInt(v, 10);
8936                      if(w){ val += w; }
8937                 }
8938             }
8939             return val;
8940         },
8941
8942         /**
8943          * Creates a proxy element of this element
8944          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8945          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8946          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8947          * @return {Roo.Element} The new proxy element
8948          */
8949         createProxy : function(config, renderTo, matchBox){
8950             if(renderTo){
8951                 renderTo = Roo.getDom(renderTo);
8952             }else{
8953                 renderTo = document.body;
8954             }
8955             config = typeof config == "object" ?
8956                 config : {tag : "div", cls: config};
8957             var proxy = Roo.DomHelper.append(renderTo, config, true);
8958             if(matchBox){
8959                proxy.setBox(this.getBox());
8960             }
8961             return proxy;
8962         },
8963
8964         /**
8965          * Puts a mask over this element to disable user interaction. Requires core.css.
8966          * This method can only be applied to elements which accept child nodes.
8967          * @param {String} msg (optional) A message to display in the mask
8968          * @param {String} msgCls (optional) A css class to apply to the msg element
8969          * @return {Element} The mask  element
8970          */
8971         mask : function(msg, msgCls)
8972         {
8973             if(this.getStyle("position") == "static"){
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     if (typeof(depreciated_tpl) == 'undefined') {
24857         // new way.. - universal constructor.
24858         Roo.apply(this, config);
24859         this.el  = Roo.get(this.el);
24860     } else {
24861         // old format..
24862         this.el  = Roo.get(config);
24863         this.tpl = depreciated_tpl;
24864         Roo.apply(this, depreciated_config);
24865     }
24866     this.wrapEl  = this.el.wrap().wrap();
24867     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24868     
24869     
24870     if(typeof(this.tpl) == "string"){
24871         this.tpl = new Roo.Template(this.tpl);
24872     } else {
24873         // support xtype ctors..
24874         this.tpl = new Roo.factory(this.tpl, Roo);
24875     }
24876     
24877     
24878     this.tpl.compile();
24879    
24880   
24881     
24882      
24883     /** @private */
24884     this.addEvents({
24885         /**
24886          * @event beforeclick
24887          * Fires before a click is processed. Returns false to cancel the default action.
24888          * @param {Roo.View} this
24889          * @param {Number} index The index of the target node
24890          * @param {HTMLElement} node The target node
24891          * @param {Roo.EventObject} e The raw event object
24892          */
24893             "beforeclick" : true,
24894         /**
24895          * @event click
24896          * Fires when a template node is clicked.
24897          * @param {Roo.View} this
24898          * @param {Number} index The index of the target node
24899          * @param {HTMLElement} node The target node
24900          * @param {Roo.EventObject} e The raw event object
24901          */
24902             "click" : true,
24903         /**
24904          * @event dblclick
24905          * Fires when a template node is double clicked.
24906          * @param {Roo.View} this
24907          * @param {Number} index The index of the target node
24908          * @param {HTMLElement} node The target node
24909          * @param {Roo.EventObject} e The raw event object
24910          */
24911             "dblclick" : true,
24912         /**
24913          * @event contextmenu
24914          * Fires when a template node is right clicked.
24915          * @param {Roo.View} this
24916          * @param {Number} index The index of the target node
24917          * @param {HTMLElement} node The target node
24918          * @param {Roo.EventObject} e The raw event object
24919          */
24920             "contextmenu" : true,
24921         /**
24922          * @event selectionchange
24923          * Fires when the selected nodes change.
24924          * @param {Roo.View} this
24925          * @param {Array} selections Array of the selected nodes
24926          */
24927             "selectionchange" : true,
24928     
24929         /**
24930          * @event beforeselect
24931          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24932          * @param {Roo.View} this
24933          * @param {HTMLElement} node The node to be selected
24934          * @param {Array} selections Array of currently selected nodes
24935          */
24936             "beforeselect" : true,
24937         /**
24938          * @event preparedata
24939          * Fires on every row to render, to allow you to change the data.
24940          * @param {Roo.View} this
24941          * @param {Object} data to be rendered (change this)
24942          */
24943           "preparedata" : true
24944           
24945           
24946         });
24947
24948
24949
24950     this.el.on({
24951         "click": this.onClick,
24952         "dblclick": this.onDblClick,
24953         "contextmenu": this.onContextMenu,
24954         scope:this
24955     });
24956
24957     this.selections = [];
24958     this.nodes = [];
24959     this.cmp = new Roo.CompositeElementLite([]);
24960     if(this.store){
24961         this.store = Roo.factory(this.store, Roo.data);
24962         this.setStore(this.store, true);
24963     }
24964     
24965     if ( this.footer && this.footer.xtype) {
24966            
24967          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24968         
24969         this.footer.dataSource = this.store
24970         this.footer.container = fctr;
24971         this.footer = Roo.factory(this.footer, Roo);
24972         fctr.insertFirst(this.el);
24973         
24974         // this is a bit insane - as the paging toolbar seems to detach the el..
24975 //        dom.parentNode.parentNode.parentNode
24976          // they get detached?
24977     }
24978     
24979     
24980     Roo.View.superclass.constructor.call(this);
24981     
24982     
24983 };
24984
24985 Roo.extend(Roo.View, Roo.util.Observable, {
24986     
24987      /**
24988      * @cfg {Roo.data.Store} store Data store to load data from.
24989      */
24990     store : false,
24991     
24992     /**
24993      * @cfg {String|Roo.Element} el The container element.
24994      */
24995     el : '',
24996     
24997     /**
24998      * @cfg {String|Roo.Template} tpl The template used by this View 
24999      */
25000     tpl : false,
25001     /**
25002      * @cfg {String} dataName the named area of the template to use as the data area
25003      *                          Works with domtemplates roo-name="name"
25004      */
25005     dataName: false,
25006     /**
25007      * @cfg {String} selectedClass The css class to add to selected nodes
25008      */
25009     selectedClass : "x-view-selected",
25010      /**
25011      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25012      */
25013     emptyText : "",
25014     
25015     /**
25016      * @cfg {String} text to display on mask (default Loading)
25017      */
25018     mask : false,
25019     /**
25020      * @cfg {Boolean} multiSelect Allow multiple selection
25021      */
25022     multiSelect : false,
25023     /**
25024      * @cfg {Boolean} singleSelect Allow single selection
25025      */
25026     singleSelect:  false,
25027     
25028     /**
25029      * @cfg {Boolean} toggleSelect - selecting 
25030      */
25031     toggleSelect : false,
25032     
25033     /**
25034      * Returns the element this view is bound to.
25035      * @return {Roo.Element}
25036      */
25037     getEl : function(){
25038         return this.wrapEl;
25039     },
25040     
25041     
25042
25043     /**
25044      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25045      */
25046     refresh : function(){
25047         Roo.log('refresh');
25048         var t = this.tpl;
25049         
25050         // if we are using something like 'domtemplate', then
25051         // the what gets used is:
25052         // t.applySubtemplate(NAME, data, wrapping data..)
25053         // the outer template then get' applied with
25054         //     the store 'extra data'
25055         // and the body get's added to the
25056         //      roo-name="data" node?
25057         //      <span class='roo-tpl-{name}'></span> ?????
25058         
25059         
25060         
25061         this.clearSelections();
25062         this.el.update("");
25063         var html = [];
25064         var records = this.store.getRange();
25065         if(records.length < 1) {
25066             
25067             // is this valid??  = should it render a template??
25068             
25069             this.el.update(this.emptyText);
25070             return;
25071         }
25072         var el = this.el;
25073         if (this.dataName) {
25074             this.el.update(t.apply(this.store.meta)); //????
25075             el = this.el.child('.roo-tpl-' + this.dataName);
25076         }
25077         
25078         for(var i = 0, len = records.length; i < len; i++){
25079             var data = this.prepareData(records[i].data, i, records[i]);
25080             this.fireEvent("preparedata", this, data, i, records[i]);
25081             html[html.length] = Roo.util.Format.trim(
25082                 this.dataName ?
25083                     t.applySubtemplate(this.dataName, data, this.store.meta) :
25084                     t.apply(data)
25085             );
25086         }
25087         
25088         
25089         
25090         el.update(html.join(""));
25091         this.nodes = el.dom.childNodes;
25092         this.updateIndexes(0);
25093     },
25094     
25095
25096     /**
25097      * Function to override to reformat the data that is sent to
25098      * the template for each node.
25099      * DEPRICATED - use the preparedata event handler.
25100      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25101      * a JSON object for an UpdateManager bound view).
25102      */
25103     prepareData : function(data, index, record)
25104     {
25105         this.fireEvent("preparedata", this, data, index, record);
25106         return data;
25107     },
25108
25109     onUpdate : function(ds, record){
25110          Roo.log('on update');   
25111         this.clearSelections();
25112         var index = this.store.indexOf(record);
25113         var n = this.nodes[index];
25114         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25115         n.parentNode.removeChild(n);
25116         this.updateIndexes(index, index);
25117     },
25118
25119     
25120     
25121 // --------- FIXME     
25122     onAdd : function(ds, records, index)
25123     {
25124         Roo.log(['on Add', ds, records, index] );        
25125         this.clearSelections();
25126         if(this.nodes.length == 0){
25127             this.refresh();
25128             return;
25129         }
25130         var n = this.nodes[index];
25131         for(var i = 0, len = records.length; i < len; i++){
25132             var d = this.prepareData(records[i].data, i, records[i]);
25133             if(n){
25134                 this.tpl.insertBefore(n, d);
25135             }else{
25136                 
25137                 this.tpl.append(this.el, d);
25138             }
25139         }
25140         this.updateIndexes(index);
25141     },
25142
25143     onRemove : function(ds, record, index){
25144         Roo.log('onRemove');
25145         this.clearSelections();
25146         var el = this.dataName  ?
25147             this.el.child('.roo-tpl-' + this.dataName) :
25148             this.el; 
25149         
25150         el.dom.removeChild(this.nodes[index]);
25151         this.updateIndexes(index);
25152     },
25153
25154     /**
25155      * Refresh an individual node.
25156      * @param {Number} index
25157      */
25158     refreshNode : function(index){
25159         this.onUpdate(this.store, this.store.getAt(index));
25160     },
25161
25162     updateIndexes : function(startIndex, endIndex){
25163         var ns = this.nodes;
25164         startIndex = startIndex || 0;
25165         endIndex = endIndex || ns.length - 1;
25166         for(var i = startIndex; i <= endIndex; i++){
25167             ns[i].nodeIndex = i;
25168         }
25169     },
25170
25171     /**
25172      * Changes the data store this view uses and refresh the view.
25173      * @param {Store} store
25174      */
25175     setStore : function(store, initial){
25176         if(!initial && this.store){
25177             this.store.un("datachanged", this.refresh);
25178             this.store.un("add", this.onAdd);
25179             this.store.un("remove", this.onRemove);
25180             this.store.un("update", this.onUpdate);
25181             this.store.un("clear", this.refresh);
25182             this.store.un("beforeload", this.onBeforeLoad);
25183             this.store.un("load", this.onLoad);
25184             this.store.un("loadexception", this.onLoad);
25185         }
25186         if(store){
25187           
25188             store.on("datachanged", this.refresh, this);
25189             store.on("add", this.onAdd, this);
25190             store.on("remove", this.onRemove, this);
25191             store.on("update", this.onUpdate, this);
25192             store.on("clear", this.refresh, this);
25193             store.on("beforeload", this.onBeforeLoad, this);
25194             store.on("load", this.onLoad, this);
25195             store.on("loadexception", this.onLoad, this);
25196         }
25197         
25198         if(store){
25199             this.refresh();
25200         }
25201     },
25202     /**
25203      * onbeforeLoad - masks the loading area.
25204      *
25205      */
25206     onBeforeLoad : function(store,opts)
25207     {
25208          Roo.log('onBeforeLoad');   
25209         if (!opts.add) {
25210             this.el.update("");
25211         }
25212         this.el.mask(this.mask ? this.mask : "Loading" ); 
25213     },
25214     onLoad : function ()
25215     {
25216         this.el.unmask();
25217     },
25218     
25219
25220     /**
25221      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25222      * @param {HTMLElement} node
25223      * @return {HTMLElement} The template node
25224      */
25225     findItemFromChild : function(node){
25226         var el = this.dataName  ?
25227             this.el.child('.roo-tpl-' + this.dataName,true) :
25228             this.el.dom; 
25229         
25230         if(!node || node.parentNode == el){
25231                     return node;
25232             }
25233             var p = node.parentNode;
25234             while(p && p != el){
25235             if(p.parentNode == el){
25236                 return p;
25237             }
25238             p = p.parentNode;
25239         }
25240             return null;
25241     },
25242
25243     /** @ignore */
25244     onClick : function(e){
25245         var item = this.findItemFromChild(e.getTarget());
25246         if(item){
25247             var index = this.indexOf(item);
25248             if(this.onItemClick(item, index, e) !== false){
25249                 this.fireEvent("click", this, index, item, e);
25250             }
25251         }else{
25252             this.clearSelections();
25253         }
25254     },
25255
25256     /** @ignore */
25257     onContextMenu : function(e){
25258         var item = this.findItemFromChild(e.getTarget());
25259         if(item){
25260             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25261         }
25262     },
25263
25264     /** @ignore */
25265     onDblClick : function(e){
25266         var item = this.findItemFromChild(e.getTarget());
25267         if(item){
25268             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25269         }
25270     },
25271
25272     onItemClick : function(item, index, e)
25273     {
25274         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25275             return false;
25276         }
25277         if (this.toggleSelect) {
25278             var m = this.isSelected(item) ? 'unselect' : 'select';
25279             Roo.log(m);
25280             var _t = this;
25281             _t[m](item, true, false);
25282             return true;
25283         }
25284         if(this.multiSelect || this.singleSelect){
25285             if(this.multiSelect && e.shiftKey && this.lastSelection){
25286                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25287             }else{
25288                 this.select(item, this.multiSelect && e.ctrlKey);
25289                 this.lastSelection = item;
25290             }
25291             e.preventDefault();
25292         }
25293         return true;
25294     },
25295
25296     /**
25297      * Get the number of selected nodes.
25298      * @return {Number}
25299      */
25300     getSelectionCount : function(){
25301         return this.selections.length;
25302     },
25303
25304     /**
25305      * Get the currently selected nodes.
25306      * @return {Array} An array of HTMLElements
25307      */
25308     getSelectedNodes : function(){
25309         return this.selections;
25310     },
25311
25312     /**
25313      * Get the indexes of the selected nodes.
25314      * @return {Array}
25315      */
25316     getSelectedIndexes : function(){
25317         var indexes = [], s = this.selections;
25318         for(var i = 0, len = s.length; i < len; i++){
25319             indexes.push(s[i].nodeIndex);
25320         }
25321         return indexes;
25322     },
25323
25324     /**
25325      * Clear all selections
25326      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25327      */
25328     clearSelections : function(suppressEvent){
25329         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25330             this.cmp.elements = this.selections;
25331             this.cmp.removeClass(this.selectedClass);
25332             this.selections = [];
25333             if(!suppressEvent){
25334                 this.fireEvent("selectionchange", this, this.selections);
25335             }
25336         }
25337     },
25338
25339     /**
25340      * Returns true if the passed node is selected
25341      * @param {HTMLElement/Number} node The node or node index
25342      * @return {Boolean}
25343      */
25344     isSelected : function(node){
25345         var s = this.selections;
25346         if(s.length < 1){
25347             return false;
25348         }
25349         node = this.getNode(node);
25350         return s.indexOf(node) !== -1;
25351     },
25352
25353     /**
25354      * Selects nodes.
25355      * @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
25356      * @param {Boolean} keepExisting (optional) true to keep existing selections
25357      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25358      */
25359     select : function(nodeInfo, keepExisting, suppressEvent){
25360         if(nodeInfo instanceof Array){
25361             if(!keepExisting){
25362                 this.clearSelections(true);
25363             }
25364             for(var i = 0, len = nodeInfo.length; i < len; i++){
25365                 this.select(nodeInfo[i], true, true);
25366             }
25367             return;
25368         } 
25369         var node = this.getNode(nodeInfo);
25370         if(!node || this.isSelected(node)){
25371             return; // already selected.
25372         }
25373         if(!keepExisting){
25374             this.clearSelections(true);
25375         }
25376         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25377             Roo.fly(node).addClass(this.selectedClass);
25378             this.selections.push(node);
25379             if(!suppressEvent){
25380                 this.fireEvent("selectionchange", this, this.selections);
25381             }
25382         }
25383         
25384         
25385     },
25386       /**
25387      * Unselects nodes.
25388      * @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
25389      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25390      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25391      */
25392     unselect : function(nodeInfo, keepExisting, suppressEvent)
25393     {
25394         if(nodeInfo instanceof Array){
25395             Roo.each(this.selections, function(s) {
25396                 this.unselect(s, nodeInfo);
25397             }, this);
25398             return;
25399         }
25400         var node = this.getNode(nodeInfo);
25401         if(!node || !this.isSelected(node)){
25402             Roo.log("not selected");
25403             return; // not selected.
25404         }
25405         // fireevent???
25406         var ns = [];
25407         Roo.each(this.selections, function(s) {
25408             if (s == node ) {
25409                 Roo.fly(node).removeClass(this.selectedClass);
25410
25411                 return;
25412             }
25413             ns.push(s);
25414         },this);
25415         
25416         this.selections= ns;
25417         this.fireEvent("selectionchange", this, this.selections);
25418     },
25419
25420     /**
25421      * Gets a template node.
25422      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25423      * @return {HTMLElement} The node or null if it wasn't found
25424      */
25425     getNode : function(nodeInfo){
25426         if(typeof nodeInfo == "string"){
25427             return document.getElementById(nodeInfo);
25428         }else if(typeof nodeInfo == "number"){
25429             return this.nodes[nodeInfo];
25430         }
25431         return nodeInfo;
25432     },
25433
25434     /**
25435      * Gets a range template nodes.
25436      * @param {Number} startIndex
25437      * @param {Number} endIndex
25438      * @return {Array} An array of nodes
25439      */
25440     getNodes : function(start, end){
25441         var ns = this.nodes;
25442         start = start || 0;
25443         end = typeof end == "undefined" ? ns.length - 1 : end;
25444         var nodes = [];
25445         if(start <= end){
25446             for(var i = start; i <= end; i++){
25447                 nodes.push(ns[i]);
25448             }
25449         } else{
25450             for(var i = start; i >= end; i--){
25451                 nodes.push(ns[i]);
25452             }
25453         }
25454         return nodes;
25455     },
25456
25457     /**
25458      * Finds the index of the passed node
25459      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25460      * @return {Number} The index of the node or -1
25461      */
25462     indexOf : function(node){
25463         node = this.getNode(node);
25464         if(typeof node.nodeIndex == "number"){
25465             return node.nodeIndex;
25466         }
25467         var ns = this.nodes;
25468         for(var i = 0, len = ns.length; i < len; i++){
25469             if(ns[i] == node){
25470                 return i;
25471             }
25472         }
25473         return -1;
25474     }
25475 });
25476 /*
25477  * Based on:
25478  * Ext JS Library 1.1.1
25479  * Copyright(c) 2006-2007, Ext JS, LLC.
25480  *
25481  * Originally Released Under LGPL - original licence link has changed is not relivant.
25482  *
25483  * Fork - LGPL
25484  * <script type="text/javascript">
25485  */
25486
25487 /**
25488  * @class Roo.JsonView
25489  * @extends Roo.View
25490  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25491 <pre><code>
25492 var view = new Roo.JsonView({
25493     container: "my-element",
25494     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25495     multiSelect: true, 
25496     jsonRoot: "data" 
25497 });
25498
25499 // listen for node click?
25500 view.on("click", function(vw, index, node, e){
25501     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25502 });
25503
25504 // direct load of JSON data
25505 view.load("foobar.php");
25506
25507 // Example from my blog list
25508 var tpl = new Roo.Template(
25509     '&lt;div class="entry"&gt;' +
25510     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25511     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25512     "&lt;/div&gt;&lt;hr /&gt;"
25513 );
25514
25515 var moreView = new Roo.JsonView({
25516     container :  "entry-list", 
25517     template : tpl,
25518     jsonRoot: "posts"
25519 });
25520 moreView.on("beforerender", this.sortEntries, this);
25521 moreView.load({
25522     url: "/blog/get-posts.php",
25523     params: "allposts=true",
25524     text: "Loading Blog Entries..."
25525 });
25526 </code></pre>
25527
25528 * Note: old code is supported with arguments : (container, template, config)
25529
25530
25531  * @constructor
25532  * Create a new JsonView
25533  * 
25534  * @param {Object} config The config object
25535  * 
25536  */
25537 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25538     
25539     
25540     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25541
25542     var um = this.el.getUpdateManager();
25543     um.setRenderer(this);
25544     um.on("update", this.onLoad, this);
25545     um.on("failure", this.onLoadException, this);
25546
25547     /**
25548      * @event beforerender
25549      * Fires before rendering of the downloaded JSON data.
25550      * @param {Roo.JsonView} this
25551      * @param {Object} data The JSON data loaded
25552      */
25553     /**
25554      * @event load
25555      * Fires when data is loaded.
25556      * @param {Roo.JsonView} this
25557      * @param {Object} data The JSON data loaded
25558      * @param {Object} response The raw Connect response object
25559      */
25560     /**
25561      * @event loadexception
25562      * Fires when loading fails.
25563      * @param {Roo.JsonView} this
25564      * @param {Object} response The raw Connect response object
25565      */
25566     this.addEvents({
25567         'beforerender' : true,
25568         'load' : true,
25569         'loadexception' : true
25570     });
25571 };
25572 Roo.extend(Roo.JsonView, Roo.View, {
25573     /**
25574      * @type {String} The root property in the loaded JSON object that contains the data
25575      */
25576     jsonRoot : "",
25577
25578     /**
25579      * Refreshes the view.
25580      */
25581     refresh : function(){
25582         this.clearSelections();
25583         this.el.update("");
25584         var html = [];
25585         var o = this.jsonData;
25586         if(o && o.length > 0){
25587             for(var i = 0, len = o.length; i < len; i++){
25588                 var data = this.prepareData(o[i], i, o);
25589                 html[html.length] = this.tpl.apply(data);
25590             }
25591         }else{
25592             html.push(this.emptyText);
25593         }
25594         this.el.update(html.join(""));
25595         this.nodes = this.el.dom.childNodes;
25596         this.updateIndexes(0);
25597     },
25598
25599     /**
25600      * 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.
25601      * @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:
25602      <pre><code>
25603      view.load({
25604          url: "your-url.php",
25605          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25606          callback: yourFunction,
25607          scope: yourObject, //(optional scope)
25608          discardUrl: false,
25609          nocache: false,
25610          text: "Loading...",
25611          timeout: 30,
25612          scripts: false
25613      });
25614      </code></pre>
25615      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25616      * 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.
25617      * @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}
25618      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25619      * @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.
25620      */
25621     load : function(){
25622         var um = this.el.getUpdateManager();
25623         um.update.apply(um, arguments);
25624     },
25625
25626     render : function(el, response){
25627         this.clearSelections();
25628         this.el.update("");
25629         var o;
25630         try{
25631             o = Roo.util.JSON.decode(response.responseText);
25632             if(this.jsonRoot){
25633                 
25634                 o = o[this.jsonRoot];
25635             }
25636         } catch(e){
25637         }
25638         /**
25639          * The current JSON data or null
25640          */
25641         this.jsonData = o;
25642         this.beforeRender();
25643         this.refresh();
25644     },
25645
25646 /**
25647  * Get the number of records in the current JSON dataset
25648  * @return {Number}
25649  */
25650     getCount : function(){
25651         return this.jsonData ? this.jsonData.length : 0;
25652     },
25653
25654 /**
25655  * Returns the JSON object for the specified node(s)
25656  * @param {HTMLElement/Array} node The node or an array of nodes
25657  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25658  * you get the JSON object for the node
25659  */
25660     getNodeData : function(node){
25661         if(node instanceof Array){
25662             var data = [];
25663             for(var i = 0, len = node.length; i < len; i++){
25664                 data.push(this.getNodeData(node[i]));
25665             }
25666             return data;
25667         }
25668         return this.jsonData[this.indexOf(node)] || null;
25669     },
25670
25671     beforeRender : function(){
25672         this.snapshot = this.jsonData;
25673         if(this.sortInfo){
25674             this.sort.apply(this, this.sortInfo);
25675         }
25676         this.fireEvent("beforerender", this, this.jsonData);
25677     },
25678
25679     onLoad : function(el, o){
25680         this.fireEvent("load", this, this.jsonData, o);
25681     },
25682
25683     onLoadException : function(el, o){
25684         this.fireEvent("loadexception", this, o);
25685     },
25686
25687 /**
25688  * Filter the data by a specific property.
25689  * @param {String} property A property on your JSON objects
25690  * @param {String/RegExp} value Either string that the property values
25691  * should start with, or a RegExp to test against the property
25692  */
25693     filter : function(property, value){
25694         if(this.jsonData){
25695             var data = [];
25696             var ss = this.snapshot;
25697             if(typeof value == "string"){
25698                 var vlen = value.length;
25699                 if(vlen == 0){
25700                     this.clearFilter();
25701                     return;
25702                 }
25703                 value = value.toLowerCase();
25704                 for(var i = 0, len = ss.length; i < len; i++){
25705                     var o = ss[i];
25706                     if(o[property].substr(0, vlen).toLowerCase() == value){
25707                         data.push(o);
25708                     }
25709                 }
25710             } else if(value.exec){ // regex?
25711                 for(var i = 0, len = ss.length; i < len; i++){
25712                     var o = ss[i];
25713                     if(value.test(o[property])){
25714                         data.push(o);
25715                     }
25716                 }
25717             } else{
25718                 return;
25719             }
25720             this.jsonData = data;
25721             this.refresh();
25722         }
25723     },
25724
25725 /**
25726  * Filter by a function. The passed function will be called with each
25727  * object in the current dataset. If the function returns true the value is kept,
25728  * otherwise it is filtered.
25729  * @param {Function} fn
25730  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25731  */
25732     filterBy : function(fn, scope){
25733         if(this.jsonData){
25734             var data = [];
25735             var ss = this.snapshot;
25736             for(var i = 0, len = ss.length; i < len; i++){
25737                 var o = ss[i];
25738                 if(fn.call(scope || this, o)){
25739                     data.push(o);
25740                 }
25741             }
25742             this.jsonData = data;
25743             this.refresh();
25744         }
25745     },
25746
25747 /**
25748  * Clears the current filter.
25749  */
25750     clearFilter : function(){
25751         if(this.snapshot && this.jsonData != this.snapshot){
25752             this.jsonData = this.snapshot;
25753             this.refresh();
25754         }
25755     },
25756
25757
25758 /**
25759  * Sorts the data for this view and refreshes it.
25760  * @param {String} property A property on your JSON objects to sort on
25761  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25762  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25763  */
25764     sort : function(property, dir, sortType){
25765         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25766         if(this.jsonData){
25767             var p = property;
25768             var dsc = dir && dir.toLowerCase() == "desc";
25769             var f = function(o1, o2){
25770                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25771                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25772                 ;
25773                 if(v1 < v2){
25774                     return dsc ? +1 : -1;
25775                 } else if(v1 > v2){
25776                     return dsc ? -1 : +1;
25777                 } else{
25778                     return 0;
25779                 }
25780             };
25781             this.jsonData.sort(f);
25782             this.refresh();
25783             if(this.jsonData != this.snapshot){
25784                 this.snapshot.sort(f);
25785             }
25786         }
25787     }
25788 });/*
25789  * Based on:
25790  * Ext JS Library 1.1.1
25791  * Copyright(c) 2006-2007, Ext JS, LLC.
25792  *
25793  * Originally Released Under LGPL - original licence link has changed is not relivant.
25794  *
25795  * Fork - LGPL
25796  * <script type="text/javascript">
25797  */
25798  
25799
25800 /**
25801  * @class Roo.ColorPalette
25802  * @extends Roo.Component
25803  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25804  * Here's an example of typical usage:
25805  * <pre><code>
25806 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25807 cp.render('my-div');
25808
25809 cp.on('select', function(palette, selColor){
25810     // do something with selColor
25811 });
25812 </code></pre>
25813  * @constructor
25814  * Create a new ColorPalette
25815  * @param {Object} config The config object
25816  */
25817 Roo.ColorPalette = function(config){
25818     Roo.ColorPalette.superclass.constructor.call(this, config);
25819     this.addEvents({
25820         /**
25821              * @event select
25822              * Fires when a color is selected
25823              * @param {ColorPalette} this
25824              * @param {String} color The 6-digit color hex code (without the # symbol)
25825              */
25826         select: true
25827     });
25828
25829     if(this.handler){
25830         this.on("select", this.handler, this.scope, true);
25831     }
25832 };
25833 Roo.extend(Roo.ColorPalette, Roo.Component, {
25834     /**
25835      * @cfg {String} itemCls
25836      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25837      */
25838     itemCls : "x-color-palette",
25839     /**
25840      * @cfg {String} value
25841      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25842      * the hex codes are case-sensitive.
25843      */
25844     value : null,
25845     clickEvent:'click',
25846     // private
25847     ctype: "Roo.ColorPalette",
25848
25849     /**
25850      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25851      */
25852     allowReselect : false,
25853
25854     /**
25855      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25856      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25857      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25858      * of colors with the width setting until the box is symmetrical.</p>
25859      * <p>You can override individual colors if needed:</p>
25860      * <pre><code>
25861 var cp = new Roo.ColorPalette();
25862 cp.colors[0] = "FF0000";  // change the first box to red
25863 </code></pre>
25864
25865 Or you can provide a custom array of your own for complete control:
25866 <pre><code>
25867 var cp = new Roo.ColorPalette();
25868 cp.colors = ["000000", "993300", "333300"];
25869 </code></pre>
25870      * @type Array
25871      */
25872     colors : [
25873         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25874         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25875         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25876         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25877         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25878     ],
25879
25880     // private
25881     onRender : function(container, position){
25882         var t = new Roo.MasterTemplate(
25883             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25884         );
25885         var c = this.colors;
25886         for(var i = 0, len = c.length; i < len; i++){
25887             t.add([c[i]]);
25888         }
25889         var el = document.createElement("div");
25890         el.className = this.itemCls;
25891         t.overwrite(el);
25892         container.dom.insertBefore(el, position);
25893         this.el = Roo.get(el);
25894         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25895         if(this.clickEvent != 'click'){
25896             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25897         }
25898     },
25899
25900     // private
25901     afterRender : function(){
25902         Roo.ColorPalette.superclass.afterRender.call(this);
25903         if(this.value){
25904             var s = this.value;
25905             this.value = null;
25906             this.select(s);
25907         }
25908     },
25909
25910     // private
25911     handleClick : function(e, t){
25912         e.preventDefault();
25913         if(!this.disabled){
25914             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25915             this.select(c.toUpperCase());
25916         }
25917     },
25918
25919     /**
25920      * Selects the specified color in the palette (fires the select event)
25921      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25922      */
25923     select : function(color){
25924         color = color.replace("#", "");
25925         if(color != this.value || this.allowReselect){
25926             var el = this.el;
25927             if(this.value){
25928                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25929             }
25930             el.child("a.color-"+color).addClass("x-color-palette-sel");
25931             this.value = color;
25932             this.fireEvent("select", this, color);
25933         }
25934     }
25935 });/*
25936  * Based on:
25937  * Ext JS Library 1.1.1
25938  * Copyright(c) 2006-2007, Ext JS, LLC.
25939  *
25940  * Originally Released Under LGPL - original licence link has changed is not relivant.
25941  *
25942  * Fork - LGPL
25943  * <script type="text/javascript">
25944  */
25945  
25946 /**
25947  * @class Roo.DatePicker
25948  * @extends Roo.Component
25949  * Simple date picker class.
25950  * @constructor
25951  * Create a new DatePicker
25952  * @param {Object} config The config object
25953  */
25954 Roo.DatePicker = function(config){
25955     Roo.DatePicker.superclass.constructor.call(this, config);
25956
25957     this.value = config && config.value ?
25958                  config.value.clearTime() : new Date().clearTime();
25959
25960     this.addEvents({
25961         /**
25962              * @event select
25963              * Fires when a date is selected
25964              * @param {DatePicker} this
25965              * @param {Date} date The selected date
25966              */
25967         'select': true,
25968         /**
25969              * @event monthchange
25970              * Fires when the displayed month changes 
25971              * @param {DatePicker} this
25972              * @param {Date} date The selected month
25973              */
25974         'monthchange': true
25975     });
25976
25977     if(this.handler){
25978         this.on("select", this.handler,  this.scope || this);
25979     }
25980     // build the disabledDatesRE
25981     if(!this.disabledDatesRE && this.disabledDates){
25982         var dd = this.disabledDates;
25983         var re = "(?:";
25984         for(var i = 0; i < dd.length; i++){
25985             re += dd[i];
25986             if(i != dd.length-1) re += "|";
25987         }
25988         this.disabledDatesRE = new RegExp(re + ")");
25989     }
25990 };
25991
25992 Roo.extend(Roo.DatePicker, Roo.Component, {
25993     /**
25994      * @cfg {String} todayText
25995      * The text to display on the button that selects the current date (defaults to "Today")
25996      */
25997     todayText : "Today",
25998     /**
25999      * @cfg {String} okText
26000      * The text to display on the ok button
26001      */
26002     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26003     /**
26004      * @cfg {String} cancelText
26005      * The text to display on the cancel button
26006      */
26007     cancelText : "Cancel",
26008     /**
26009      * @cfg {String} todayTip
26010      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26011      */
26012     todayTip : "{0} (Spacebar)",
26013     /**
26014      * @cfg {Date} minDate
26015      * Minimum allowable date (JavaScript date object, defaults to null)
26016      */
26017     minDate : null,
26018     /**
26019      * @cfg {Date} maxDate
26020      * Maximum allowable date (JavaScript date object, defaults to null)
26021      */
26022     maxDate : null,
26023     /**
26024      * @cfg {String} minText
26025      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26026      */
26027     minText : "This date is before the minimum date",
26028     /**
26029      * @cfg {String} maxText
26030      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26031      */
26032     maxText : "This date is after the maximum date",
26033     /**
26034      * @cfg {String} format
26035      * The default date format string which can be overriden for localization support.  The format must be
26036      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26037      */
26038     format : "m/d/y",
26039     /**
26040      * @cfg {Array} disabledDays
26041      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26042      */
26043     disabledDays : null,
26044     /**
26045      * @cfg {String} disabledDaysText
26046      * The tooltip to display when the date falls on a disabled day (defaults to "")
26047      */
26048     disabledDaysText : "",
26049     /**
26050      * @cfg {RegExp} disabledDatesRE
26051      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26052      */
26053     disabledDatesRE : null,
26054     /**
26055      * @cfg {String} disabledDatesText
26056      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26057      */
26058     disabledDatesText : "",
26059     /**
26060      * @cfg {Boolean} constrainToViewport
26061      * True to constrain the date picker to the viewport (defaults to true)
26062      */
26063     constrainToViewport : true,
26064     /**
26065      * @cfg {Array} monthNames
26066      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26067      */
26068     monthNames : Date.monthNames,
26069     /**
26070      * @cfg {Array} dayNames
26071      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26072      */
26073     dayNames : Date.dayNames,
26074     /**
26075      * @cfg {String} nextText
26076      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26077      */
26078     nextText: 'Next Month (Control+Right)',
26079     /**
26080      * @cfg {String} prevText
26081      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26082      */
26083     prevText: 'Previous Month (Control+Left)',
26084     /**
26085      * @cfg {String} monthYearText
26086      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26087      */
26088     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26089     /**
26090      * @cfg {Number} startDay
26091      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26092      */
26093     startDay : 0,
26094     /**
26095      * @cfg {Bool} showClear
26096      * Show a clear button (usefull for date form elements that can be blank.)
26097      */
26098     
26099     showClear: false,
26100     
26101     /**
26102      * Sets the value of the date field
26103      * @param {Date} value The date to set
26104      */
26105     setValue : function(value){
26106         var old = this.value;
26107         
26108         if (typeof(value) == 'string') {
26109          
26110             value = Date.parseDate(value, this.format);
26111         }
26112         if (!value) {
26113             value = new Date();
26114         }
26115         
26116         this.value = value.clearTime(true);
26117         if(this.el){
26118             this.update(this.value);
26119         }
26120     },
26121
26122     /**
26123      * Gets the current selected value of the date field
26124      * @return {Date} The selected date
26125      */
26126     getValue : function(){
26127         return this.value;
26128     },
26129
26130     // private
26131     focus : function(){
26132         if(this.el){
26133             this.update(this.activeDate);
26134         }
26135     },
26136
26137     // privateval
26138     onRender : function(container, position){
26139         
26140         var m = [
26141              '<table cellspacing="0">',
26142                 '<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>',
26143                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26144         var dn = this.dayNames;
26145         for(var i = 0; i < 7; i++){
26146             var d = this.startDay+i;
26147             if(d > 6){
26148                 d = d-7;
26149             }
26150             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26151         }
26152         m[m.length] = "</tr></thead><tbody><tr>";
26153         for(var i = 0; i < 42; i++) {
26154             if(i % 7 == 0 && i != 0){
26155                 m[m.length] = "</tr><tr>";
26156             }
26157             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26158         }
26159         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26160             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26161
26162         var el = document.createElement("div");
26163         el.className = "x-date-picker";
26164         el.innerHTML = m.join("");
26165
26166         container.dom.insertBefore(el, position);
26167
26168         this.el = Roo.get(el);
26169         this.eventEl = Roo.get(el.firstChild);
26170
26171         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26172             handler: this.showPrevMonth,
26173             scope: this,
26174             preventDefault:true,
26175             stopDefault:true
26176         });
26177
26178         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26179             handler: this.showNextMonth,
26180             scope: this,
26181             preventDefault:true,
26182             stopDefault:true
26183         });
26184
26185         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26186
26187         this.monthPicker = this.el.down('div.x-date-mp');
26188         this.monthPicker.enableDisplayMode('block');
26189         
26190         var kn = new Roo.KeyNav(this.eventEl, {
26191             "left" : function(e){
26192                 e.ctrlKey ?
26193                     this.showPrevMonth() :
26194                     this.update(this.activeDate.add("d", -1));
26195             },
26196
26197             "right" : function(e){
26198                 e.ctrlKey ?
26199                     this.showNextMonth() :
26200                     this.update(this.activeDate.add("d", 1));
26201             },
26202
26203             "up" : function(e){
26204                 e.ctrlKey ?
26205                     this.showNextYear() :
26206                     this.update(this.activeDate.add("d", -7));
26207             },
26208
26209             "down" : function(e){
26210                 e.ctrlKey ?
26211                     this.showPrevYear() :
26212                     this.update(this.activeDate.add("d", 7));
26213             },
26214
26215             "pageUp" : function(e){
26216                 this.showNextMonth();
26217             },
26218
26219             "pageDown" : function(e){
26220                 this.showPrevMonth();
26221             },
26222
26223             "enter" : function(e){
26224                 e.stopPropagation();
26225                 return true;
26226             },
26227
26228             scope : this
26229         });
26230
26231         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26232
26233         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26234
26235         this.el.unselectable();
26236         
26237         this.cells = this.el.select("table.x-date-inner tbody td");
26238         this.textNodes = this.el.query("table.x-date-inner tbody span");
26239
26240         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26241             text: "&#160;",
26242             tooltip: this.monthYearText
26243         });
26244
26245         this.mbtn.on('click', this.showMonthPicker, this);
26246         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26247
26248
26249         var today = (new Date()).dateFormat(this.format);
26250         
26251         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26252         if (this.showClear) {
26253             baseTb.add( new Roo.Toolbar.Fill());
26254         }
26255         baseTb.add({
26256             text: String.format(this.todayText, today),
26257             tooltip: String.format(this.todayTip, today),
26258             handler: this.selectToday,
26259             scope: this
26260         });
26261         
26262         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26263             
26264         //});
26265         if (this.showClear) {
26266             
26267             baseTb.add( new Roo.Toolbar.Fill());
26268             baseTb.add({
26269                 text: '&#160;',
26270                 cls: 'x-btn-icon x-btn-clear',
26271                 handler: function() {
26272                     //this.value = '';
26273                     this.fireEvent("select", this, '');
26274                 },
26275                 scope: this
26276             });
26277         }
26278         
26279         
26280         if(Roo.isIE){
26281             this.el.repaint();
26282         }
26283         this.update(this.value);
26284     },
26285
26286     createMonthPicker : function(){
26287         if(!this.monthPicker.dom.firstChild){
26288             var buf = ['<table border="0" cellspacing="0">'];
26289             for(var i = 0; i < 6; i++){
26290                 buf.push(
26291                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26292                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26293                     i == 0 ?
26294                     '<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>' :
26295                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26296                 );
26297             }
26298             buf.push(
26299                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26300                     this.okText,
26301                     '</button><button type="button" class="x-date-mp-cancel">',
26302                     this.cancelText,
26303                     '</button></td></tr>',
26304                 '</table>'
26305             );
26306             this.monthPicker.update(buf.join(''));
26307             this.monthPicker.on('click', this.onMonthClick, this);
26308             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26309
26310             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26311             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26312
26313             this.mpMonths.each(function(m, a, i){
26314                 i += 1;
26315                 if((i%2) == 0){
26316                     m.dom.xmonth = 5 + Math.round(i * .5);
26317                 }else{
26318                     m.dom.xmonth = Math.round((i-1) * .5);
26319                 }
26320             });
26321         }
26322     },
26323
26324     showMonthPicker : function(){
26325         this.createMonthPicker();
26326         var size = this.el.getSize();
26327         this.monthPicker.setSize(size);
26328         this.monthPicker.child('table').setSize(size);
26329
26330         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26331         this.updateMPMonth(this.mpSelMonth);
26332         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26333         this.updateMPYear(this.mpSelYear);
26334
26335         this.monthPicker.slideIn('t', {duration:.2});
26336     },
26337
26338     updateMPYear : function(y){
26339         this.mpyear = y;
26340         var ys = this.mpYears.elements;
26341         for(var i = 1; i <= 10; i++){
26342             var td = ys[i-1], y2;
26343             if((i%2) == 0){
26344                 y2 = y + Math.round(i * .5);
26345                 td.firstChild.innerHTML = y2;
26346                 td.xyear = y2;
26347             }else{
26348                 y2 = y - (5-Math.round(i * .5));
26349                 td.firstChild.innerHTML = y2;
26350                 td.xyear = y2;
26351             }
26352             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26353         }
26354     },
26355
26356     updateMPMonth : function(sm){
26357         this.mpMonths.each(function(m, a, i){
26358             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26359         });
26360     },
26361
26362     selectMPMonth: function(m){
26363         
26364     },
26365
26366     onMonthClick : function(e, t){
26367         e.stopEvent();
26368         var el = new Roo.Element(t), pn;
26369         if(el.is('button.x-date-mp-cancel')){
26370             this.hideMonthPicker();
26371         }
26372         else if(el.is('button.x-date-mp-ok')){
26373             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26374             this.hideMonthPicker();
26375         }
26376         else if(pn = el.up('td.x-date-mp-month', 2)){
26377             this.mpMonths.removeClass('x-date-mp-sel');
26378             pn.addClass('x-date-mp-sel');
26379             this.mpSelMonth = pn.dom.xmonth;
26380         }
26381         else if(pn = el.up('td.x-date-mp-year', 2)){
26382             this.mpYears.removeClass('x-date-mp-sel');
26383             pn.addClass('x-date-mp-sel');
26384             this.mpSelYear = pn.dom.xyear;
26385         }
26386         else if(el.is('a.x-date-mp-prev')){
26387             this.updateMPYear(this.mpyear-10);
26388         }
26389         else if(el.is('a.x-date-mp-next')){
26390             this.updateMPYear(this.mpyear+10);
26391         }
26392     },
26393
26394     onMonthDblClick : function(e, t){
26395         e.stopEvent();
26396         var el = new Roo.Element(t), pn;
26397         if(pn = el.up('td.x-date-mp-month', 2)){
26398             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26399             this.hideMonthPicker();
26400         }
26401         else if(pn = el.up('td.x-date-mp-year', 2)){
26402             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26403             this.hideMonthPicker();
26404         }
26405     },
26406
26407     hideMonthPicker : function(disableAnim){
26408         if(this.monthPicker){
26409             if(disableAnim === true){
26410                 this.monthPicker.hide();
26411             }else{
26412                 this.monthPicker.slideOut('t', {duration:.2});
26413             }
26414         }
26415     },
26416
26417     // private
26418     showPrevMonth : function(e){
26419         this.update(this.activeDate.add("mo", -1));
26420     },
26421
26422     // private
26423     showNextMonth : function(e){
26424         this.update(this.activeDate.add("mo", 1));
26425     },
26426
26427     // private
26428     showPrevYear : function(){
26429         this.update(this.activeDate.add("y", -1));
26430     },
26431
26432     // private
26433     showNextYear : function(){
26434         this.update(this.activeDate.add("y", 1));
26435     },
26436
26437     // private
26438     handleMouseWheel : function(e){
26439         var delta = e.getWheelDelta();
26440         if(delta > 0){
26441             this.showPrevMonth();
26442             e.stopEvent();
26443         } else if(delta < 0){
26444             this.showNextMonth();
26445             e.stopEvent();
26446         }
26447     },
26448
26449     // private
26450     handleDateClick : function(e, t){
26451         e.stopEvent();
26452         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26453             this.setValue(new Date(t.dateValue));
26454             this.fireEvent("select", this, this.value);
26455         }
26456     },
26457
26458     // private
26459     selectToday : function(){
26460         this.setValue(new Date().clearTime());
26461         this.fireEvent("select", this, this.value);
26462     },
26463
26464     // private
26465     update : function(date)
26466     {
26467         var vd = this.activeDate;
26468         this.activeDate = date;
26469         if(vd && this.el){
26470             var t = date.getTime();
26471             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26472                 this.cells.removeClass("x-date-selected");
26473                 this.cells.each(function(c){
26474                    if(c.dom.firstChild.dateValue == t){
26475                        c.addClass("x-date-selected");
26476                        setTimeout(function(){
26477                             try{c.dom.firstChild.focus();}catch(e){}
26478                        }, 50);
26479                        return false;
26480                    }
26481                 });
26482                 return;
26483             }
26484         }
26485         
26486         var days = date.getDaysInMonth();
26487         var firstOfMonth = date.getFirstDateOfMonth();
26488         var startingPos = firstOfMonth.getDay()-this.startDay;
26489
26490         if(startingPos <= this.startDay){
26491             startingPos += 7;
26492         }
26493
26494         var pm = date.add("mo", -1);
26495         var prevStart = pm.getDaysInMonth()-startingPos;
26496
26497         var cells = this.cells.elements;
26498         var textEls = this.textNodes;
26499         days += startingPos;
26500
26501         // convert everything to numbers so it's fast
26502         var day = 86400000;
26503         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26504         var today = new Date().clearTime().getTime();
26505         var sel = date.clearTime().getTime();
26506         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26507         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26508         var ddMatch = this.disabledDatesRE;
26509         var ddText = this.disabledDatesText;
26510         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26511         var ddaysText = this.disabledDaysText;
26512         var format = this.format;
26513
26514         var setCellClass = function(cal, cell){
26515             cell.title = "";
26516             var t = d.getTime();
26517             cell.firstChild.dateValue = t;
26518             if(t == today){
26519                 cell.className += " x-date-today";
26520                 cell.title = cal.todayText;
26521             }
26522             if(t == sel){
26523                 cell.className += " x-date-selected";
26524                 setTimeout(function(){
26525                     try{cell.firstChild.focus();}catch(e){}
26526                 }, 50);
26527             }
26528             // disabling
26529             if(t < min) {
26530                 cell.className = " x-date-disabled";
26531                 cell.title = cal.minText;
26532                 return;
26533             }
26534             if(t > max) {
26535                 cell.className = " x-date-disabled";
26536                 cell.title = cal.maxText;
26537                 return;
26538             }
26539             if(ddays){
26540                 if(ddays.indexOf(d.getDay()) != -1){
26541                     cell.title = ddaysText;
26542                     cell.className = " x-date-disabled";
26543                 }
26544             }
26545             if(ddMatch && format){
26546                 var fvalue = d.dateFormat(format);
26547                 if(ddMatch.test(fvalue)){
26548                     cell.title = ddText.replace("%0", fvalue);
26549                     cell.className = " x-date-disabled";
26550                 }
26551             }
26552         };
26553
26554         var i = 0;
26555         for(; i < startingPos; i++) {
26556             textEls[i].innerHTML = (++prevStart);
26557             d.setDate(d.getDate()+1);
26558             cells[i].className = "x-date-prevday";
26559             setCellClass(this, cells[i]);
26560         }
26561         for(; i < days; i++){
26562             intDay = i - startingPos + 1;
26563             textEls[i].innerHTML = (intDay);
26564             d.setDate(d.getDate()+1);
26565             cells[i].className = "x-date-active";
26566             setCellClass(this, cells[i]);
26567         }
26568         var extraDays = 0;
26569         for(; i < 42; i++) {
26570              textEls[i].innerHTML = (++extraDays);
26571              d.setDate(d.getDate()+1);
26572              cells[i].className = "x-date-nextday";
26573              setCellClass(this, cells[i]);
26574         }
26575
26576         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26577         this.fireEvent('monthchange', this, date);
26578         
26579         if(!this.internalRender){
26580             var main = this.el.dom.firstChild;
26581             var w = main.offsetWidth;
26582             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26583             Roo.fly(main).setWidth(w);
26584             this.internalRender = true;
26585             // opera does not respect the auto grow header center column
26586             // then, after it gets a width opera refuses to recalculate
26587             // without a second pass
26588             if(Roo.isOpera && !this.secondPass){
26589                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26590                 this.secondPass = true;
26591                 this.update.defer(10, this, [date]);
26592             }
26593         }
26594         
26595         
26596     }
26597 });        /*
26598  * Based on:
26599  * Ext JS Library 1.1.1
26600  * Copyright(c) 2006-2007, Ext JS, LLC.
26601  *
26602  * Originally Released Under LGPL - original licence link has changed is not relivant.
26603  *
26604  * Fork - LGPL
26605  * <script type="text/javascript">
26606  */
26607 /**
26608  * @class Roo.TabPanel
26609  * @extends Roo.util.Observable
26610  * A lightweight tab container.
26611  * <br><br>
26612  * Usage:
26613  * <pre><code>
26614 // basic tabs 1, built from existing content
26615 var tabs = new Roo.TabPanel("tabs1");
26616 tabs.addTab("script", "View Script");
26617 tabs.addTab("markup", "View Markup");
26618 tabs.activate("script");
26619
26620 // more advanced tabs, built from javascript
26621 var jtabs = new Roo.TabPanel("jtabs");
26622 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26623
26624 // set up the UpdateManager
26625 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26626 var updater = tab2.getUpdateManager();
26627 updater.setDefaultUrl("ajax1.htm");
26628 tab2.on('activate', updater.refresh, updater, true);
26629
26630 // Use setUrl for Ajax loading
26631 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26632 tab3.setUrl("ajax2.htm", null, true);
26633
26634 // Disabled tab
26635 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26636 tab4.disable();
26637
26638 jtabs.activate("jtabs-1");
26639  * </code></pre>
26640  * @constructor
26641  * Create a new TabPanel.
26642  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26643  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26644  */
26645 Roo.TabPanel = function(container, config){
26646     /**
26647     * The container element for this TabPanel.
26648     * @type Roo.Element
26649     */
26650     this.el = Roo.get(container, true);
26651     if(config){
26652         if(typeof config == "boolean"){
26653             this.tabPosition = config ? "bottom" : "top";
26654         }else{
26655             Roo.apply(this, config);
26656         }
26657     }
26658     if(this.tabPosition == "bottom"){
26659         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26660         this.el.addClass("x-tabs-bottom");
26661     }
26662     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26663     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26664     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26665     if(Roo.isIE){
26666         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26667     }
26668     if(this.tabPosition != "bottom"){
26669         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26670          * @type Roo.Element
26671          */
26672         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26673         this.el.addClass("x-tabs-top");
26674     }
26675     this.items = [];
26676
26677     this.bodyEl.setStyle("position", "relative");
26678
26679     this.active = null;
26680     this.activateDelegate = this.activate.createDelegate(this);
26681
26682     this.addEvents({
26683         /**
26684          * @event tabchange
26685          * Fires when the active tab changes
26686          * @param {Roo.TabPanel} this
26687          * @param {Roo.TabPanelItem} activePanel The new active tab
26688          */
26689         "tabchange": true,
26690         /**
26691          * @event beforetabchange
26692          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26693          * @param {Roo.TabPanel} this
26694          * @param {Object} e Set cancel to true on this object to cancel the tab change
26695          * @param {Roo.TabPanelItem} tab The tab being changed to
26696          */
26697         "beforetabchange" : true
26698     });
26699
26700     Roo.EventManager.onWindowResize(this.onResize, this);
26701     this.cpad = this.el.getPadding("lr");
26702     this.hiddenCount = 0;
26703
26704
26705     // toolbar on the tabbar support...
26706     if (this.toolbar) {
26707         var tcfg = this.toolbar;
26708         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26709         this.toolbar = new Roo.Toolbar(tcfg);
26710         if (Roo.isSafari) {
26711             var tbl = tcfg.container.child('table', true);
26712             tbl.setAttribute('width', '100%');
26713         }
26714         
26715     }
26716    
26717
26718
26719     Roo.TabPanel.superclass.constructor.call(this);
26720 };
26721
26722 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26723     /*
26724      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26725      */
26726     tabPosition : "top",
26727     /*
26728      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26729      */
26730     currentTabWidth : 0,
26731     /*
26732      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26733      */
26734     minTabWidth : 40,
26735     /*
26736      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26737      */
26738     maxTabWidth : 250,
26739     /*
26740      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26741      */
26742     preferredTabWidth : 175,
26743     /*
26744      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26745      */
26746     resizeTabs : false,
26747     /*
26748      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26749      */
26750     monitorResize : true,
26751     /*
26752      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26753      */
26754     toolbar : false,
26755
26756     /**
26757      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26758      * @param {String} id The id of the div to use <b>or create</b>
26759      * @param {String} text The text for the tab
26760      * @param {String} content (optional) Content to put in the TabPanelItem body
26761      * @param {Boolean} closable (optional) True to create a close icon on the tab
26762      * @return {Roo.TabPanelItem} The created TabPanelItem
26763      */
26764     addTab : function(id, text, content, closable){
26765         var item = new Roo.TabPanelItem(this, id, text, closable);
26766         this.addTabItem(item);
26767         if(content){
26768             item.setContent(content);
26769         }
26770         return item;
26771     },
26772
26773     /**
26774      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26775      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26776      * @return {Roo.TabPanelItem}
26777      */
26778     getTab : function(id){
26779         return this.items[id];
26780     },
26781
26782     /**
26783      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26784      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26785      */
26786     hideTab : function(id){
26787         var t = this.items[id];
26788         if(!t.isHidden()){
26789            t.setHidden(true);
26790            this.hiddenCount++;
26791            this.autoSizeTabs();
26792         }
26793     },
26794
26795     /**
26796      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26797      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26798      */
26799     unhideTab : function(id){
26800         var t = this.items[id];
26801         if(t.isHidden()){
26802            t.setHidden(false);
26803            this.hiddenCount--;
26804            this.autoSizeTabs();
26805         }
26806     },
26807
26808     /**
26809      * Adds an existing {@link Roo.TabPanelItem}.
26810      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26811      */
26812     addTabItem : function(item){
26813         this.items[item.id] = item;
26814         this.items.push(item);
26815         if(this.resizeTabs){
26816            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26817            this.autoSizeTabs();
26818         }else{
26819             item.autoSize();
26820         }
26821     },
26822
26823     /**
26824      * Removes a {@link Roo.TabPanelItem}.
26825      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26826      */
26827     removeTab : function(id){
26828         var items = this.items;
26829         var tab = items[id];
26830         if(!tab) { return; }
26831         var index = items.indexOf(tab);
26832         if(this.active == tab && items.length > 1){
26833             var newTab = this.getNextAvailable(index);
26834             if(newTab) {
26835                 newTab.activate();
26836             }
26837         }
26838         this.stripEl.dom.removeChild(tab.pnode.dom);
26839         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26840             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26841         }
26842         items.splice(index, 1);
26843         delete this.items[tab.id];
26844         tab.fireEvent("close", tab);
26845         tab.purgeListeners();
26846         this.autoSizeTabs();
26847     },
26848
26849     getNextAvailable : function(start){
26850         var items = this.items;
26851         var index = start;
26852         // look for a next tab that will slide over to
26853         // replace the one being removed
26854         while(index < items.length){
26855             var item = items[++index];
26856             if(item && !item.isHidden()){
26857                 return item;
26858             }
26859         }
26860         // if one isn't found select the previous tab (on the left)
26861         index = start;
26862         while(index >= 0){
26863             var item = items[--index];
26864             if(item && !item.isHidden()){
26865                 return item;
26866             }
26867         }
26868         return null;
26869     },
26870
26871     /**
26872      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26873      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26874      */
26875     disableTab : function(id){
26876         var tab = this.items[id];
26877         if(tab && this.active != tab){
26878             tab.disable();
26879         }
26880     },
26881
26882     /**
26883      * Enables a {@link Roo.TabPanelItem} that is disabled.
26884      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26885      */
26886     enableTab : function(id){
26887         var tab = this.items[id];
26888         tab.enable();
26889     },
26890
26891     /**
26892      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26893      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26894      * @return {Roo.TabPanelItem} The TabPanelItem.
26895      */
26896     activate : function(id){
26897         var tab = this.items[id];
26898         if(!tab){
26899             return null;
26900         }
26901         if(tab == this.active || tab.disabled){
26902             return tab;
26903         }
26904         var e = {};
26905         this.fireEvent("beforetabchange", this, e, tab);
26906         if(e.cancel !== true && !tab.disabled){
26907             if(this.active){
26908                 this.active.hide();
26909             }
26910             this.active = this.items[id];
26911             this.active.show();
26912             this.fireEvent("tabchange", this, this.active);
26913         }
26914         return tab;
26915     },
26916
26917     /**
26918      * Gets the active {@link Roo.TabPanelItem}.
26919      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26920      */
26921     getActiveTab : function(){
26922         return this.active;
26923     },
26924
26925     /**
26926      * Updates the tab body element to fit the height of the container element
26927      * for overflow scrolling
26928      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26929      */
26930     syncHeight : function(targetHeight){
26931         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26932         var bm = this.bodyEl.getMargins();
26933         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26934         this.bodyEl.setHeight(newHeight);
26935         return newHeight;
26936     },
26937
26938     onResize : function(){
26939         if(this.monitorResize){
26940             this.autoSizeTabs();
26941         }
26942     },
26943
26944     /**
26945      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26946      */
26947     beginUpdate : function(){
26948         this.updating = true;
26949     },
26950
26951     /**
26952      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26953      */
26954     endUpdate : function(){
26955         this.updating = false;
26956         this.autoSizeTabs();
26957     },
26958
26959     /**
26960      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26961      */
26962     autoSizeTabs : function(){
26963         var count = this.items.length;
26964         var vcount = count - this.hiddenCount;
26965         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26966         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26967         var availWidth = Math.floor(w / vcount);
26968         var b = this.stripBody;
26969         if(b.getWidth() > w){
26970             var tabs = this.items;
26971             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26972             if(availWidth < this.minTabWidth){
26973                 /*if(!this.sleft){    // incomplete scrolling code
26974                     this.createScrollButtons();
26975                 }
26976                 this.showScroll();
26977                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26978             }
26979         }else{
26980             if(this.currentTabWidth < this.preferredTabWidth){
26981                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26982             }
26983         }
26984     },
26985
26986     /**
26987      * Returns the number of tabs in this TabPanel.
26988      * @return {Number}
26989      */
26990      getCount : function(){
26991          return this.items.length;
26992      },
26993
26994     /**
26995      * Resizes all the tabs to the passed width
26996      * @param {Number} The new width
26997      */
26998     setTabWidth : function(width){
26999         this.currentTabWidth = width;
27000         for(var i = 0, len = this.items.length; i < len; i++) {
27001                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27002         }
27003     },
27004
27005     /**
27006      * Destroys this TabPanel
27007      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27008      */
27009     destroy : function(removeEl){
27010         Roo.EventManager.removeResizeListener(this.onResize, this);
27011         for(var i = 0, len = this.items.length; i < len; i++){
27012             this.items[i].purgeListeners();
27013         }
27014         if(removeEl === true){
27015             this.el.update("");
27016             this.el.remove();
27017         }
27018     }
27019 });
27020
27021 /**
27022  * @class Roo.TabPanelItem
27023  * @extends Roo.util.Observable
27024  * Represents an individual item (tab plus body) in a TabPanel.
27025  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27026  * @param {String} id The id of this TabPanelItem
27027  * @param {String} text The text for the tab of this TabPanelItem
27028  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27029  */
27030 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27031     /**
27032      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27033      * @type Roo.TabPanel
27034      */
27035     this.tabPanel = tabPanel;
27036     /**
27037      * The id for this TabPanelItem
27038      * @type String
27039      */
27040     this.id = id;
27041     /** @private */
27042     this.disabled = false;
27043     /** @private */
27044     this.text = text;
27045     /** @private */
27046     this.loaded = false;
27047     this.closable = closable;
27048
27049     /**
27050      * The body element for this TabPanelItem.
27051      * @type Roo.Element
27052      */
27053     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27054     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27055     this.bodyEl.setStyle("display", "block");
27056     this.bodyEl.setStyle("zoom", "1");
27057     this.hideAction();
27058
27059     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27060     /** @private */
27061     this.el = Roo.get(els.el, true);
27062     this.inner = Roo.get(els.inner, true);
27063     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27064     this.pnode = Roo.get(els.el.parentNode, true);
27065     this.el.on("mousedown", this.onTabMouseDown, this);
27066     this.el.on("click", this.onTabClick, this);
27067     /** @private */
27068     if(closable){
27069         var c = Roo.get(els.close, true);
27070         c.dom.title = this.closeText;
27071         c.addClassOnOver("close-over");
27072         c.on("click", this.closeClick, this);
27073      }
27074
27075     this.addEvents({
27076          /**
27077          * @event activate
27078          * Fires when this tab becomes the active tab.
27079          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27080          * @param {Roo.TabPanelItem} this
27081          */
27082         "activate": true,
27083         /**
27084          * @event beforeclose
27085          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27086          * @param {Roo.TabPanelItem} this
27087          * @param {Object} e Set cancel to true on this object to cancel the close.
27088          */
27089         "beforeclose": true,
27090         /**
27091          * @event close
27092          * Fires when this tab is closed.
27093          * @param {Roo.TabPanelItem} this
27094          */
27095          "close": true,
27096         /**
27097          * @event deactivate
27098          * Fires when this tab is no longer the active tab.
27099          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27100          * @param {Roo.TabPanelItem} this
27101          */
27102          "deactivate" : true
27103     });
27104     this.hidden = false;
27105
27106     Roo.TabPanelItem.superclass.constructor.call(this);
27107 };
27108
27109 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27110     purgeListeners : function(){
27111        Roo.util.Observable.prototype.purgeListeners.call(this);
27112        this.el.removeAllListeners();
27113     },
27114     /**
27115      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27116      */
27117     show : function(){
27118         this.pnode.addClass("on");
27119         this.showAction();
27120         if(Roo.isOpera){
27121             this.tabPanel.stripWrap.repaint();
27122         }
27123         this.fireEvent("activate", this.tabPanel, this);
27124     },
27125
27126     /**
27127      * Returns true if this tab is the active tab.
27128      * @return {Boolean}
27129      */
27130     isActive : function(){
27131         return this.tabPanel.getActiveTab() == this;
27132     },
27133
27134     /**
27135      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27136      */
27137     hide : function(){
27138         this.pnode.removeClass("on");
27139         this.hideAction();
27140         this.fireEvent("deactivate", this.tabPanel, this);
27141     },
27142
27143     hideAction : function(){
27144         this.bodyEl.hide();
27145         this.bodyEl.setStyle("position", "absolute");
27146         this.bodyEl.setLeft("-20000px");
27147         this.bodyEl.setTop("-20000px");
27148     },
27149
27150     showAction : function(){
27151         this.bodyEl.setStyle("position", "relative");
27152         this.bodyEl.setTop("");
27153         this.bodyEl.setLeft("");
27154         this.bodyEl.show();
27155     },
27156
27157     /**
27158      * Set the tooltip for the tab.
27159      * @param {String} tooltip The tab's tooltip
27160      */
27161     setTooltip : function(text){
27162         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27163             this.textEl.dom.qtip = text;
27164             this.textEl.dom.removeAttribute('title');
27165         }else{
27166             this.textEl.dom.title = text;
27167         }
27168     },
27169
27170     onTabClick : function(e){
27171         e.preventDefault();
27172         this.tabPanel.activate(this.id);
27173     },
27174
27175     onTabMouseDown : function(e){
27176         e.preventDefault();
27177         this.tabPanel.activate(this.id);
27178     },
27179
27180     getWidth : function(){
27181         return this.inner.getWidth();
27182     },
27183
27184     setWidth : function(width){
27185         var iwidth = width - this.pnode.getPadding("lr");
27186         this.inner.setWidth(iwidth);
27187         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27188         this.pnode.setWidth(width);
27189     },
27190
27191     /**
27192      * Show or hide the tab
27193      * @param {Boolean} hidden True to hide or false to show.
27194      */
27195     setHidden : function(hidden){
27196         this.hidden = hidden;
27197         this.pnode.setStyle("display", hidden ? "none" : "");
27198     },
27199
27200     /**
27201      * Returns true if this tab is "hidden"
27202      * @return {Boolean}
27203      */
27204     isHidden : function(){
27205         return this.hidden;
27206     },
27207
27208     /**
27209      * Returns the text for this tab
27210      * @return {String}
27211      */
27212     getText : function(){
27213         return this.text;
27214     },
27215
27216     autoSize : function(){
27217         //this.el.beginMeasure();
27218         this.textEl.setWidth(1);
27219         /*
27220          *  #2804 [new] Tabs in Roojs
27221          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27222          */
27223         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27224         //this.el.endMeasure();
27225     },
27226
27227     /**
27228      * Sets the text for the tab (Note: this also sets the tooltip text)
27229      * @param {String} text The tab's text and tooltip
27230      */
27231     setText : function(text){
27232         this.text = text;
27233         this.textEl.update(text);
27234         this.setTooltip(text);
27235         if(!this.tabPanel.resizeTabs){
27236             this.autoSize();
27237         }
27238     },
27239     /**
27240      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27241      */
27242     activate : function(){
27243         this.tabPanel.activate(this.id);
27244     },
27245
27246     /**
27247      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27248      */
27249     disable : function(){
27250         if(this.tabPanel.active != this){
27251             this.disabled = true;
27252             this.pnode.addClass("disabled");
27253         }
27254     },
27255
27256     /**
27257      * Enables this TabPanelItem if it was previously disabled.
27258      */
27259     enable : function(){
27260         this.disabled = false;
27261         this.pnode.removeClass("disabled");
27262     },
27263
27264     /**
27265      * Sets the content for this TabPanelItem.
27266      * @param {String} content The content
27267      * @param {Boolean} loadScripts true to look for and load scripts
27268      */
27269     setContent : function(content, loadScripts){
27270         this.bodyEl.update(content, loadScripts);
27271     },
27272
27273     /**
27274      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27275      * @return {Roo.UpdateManager} The UpdateManager
27276      */
27277     getUpdateManager : function(){
27278         return this.bodyEl.getUpdateManager();
27279     },
27280
27281     /**
27282      * Set a URL to be used to load the content for this TabPanelItem.
27283      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27284      * @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)
27285      * @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)
27286      * @return {Roo.UpdateManager} The UpdateManager
27287      */
27288     setUrl : function(url, params, loadOnce){
27289         if(this.refreshDelegate){
27290             this.un('activate', this.refreshDelegate);
27291         }
27292         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27293         this.on("activate", this.refreshDelegate);
27294         return this.bodyEl.getUpdateManager();
27295     },
27296
27297     /** @private */
27298     _handleRefresh : function(url, params, loadOnce){
27299         if(!loadOnce || !this.loaded){
27300             var updater = this.bodyEl.getUpdateManager();
27301             updater.update(url, params, this._setLoaded.createDelegate(this));
27302         }
27303     },
27304
27305     /**
27306      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27307      *   Will fail silently if the setUrl method has not been called.
27308      *   This does not activate the panel, just updates its content.
27309      */
27310     refresh : function(){
27311         if(this.refreshDelegate){
27312            this.loaded = false;
27313            this.refreshDelegate();
27314         }
27315     },
27316
27317     /** @private */
27318     _setLoaded : function(){
27319         this.loaded = true;
27320     },
27321
27322     /** @private */
27323     closeClick : function(e){
27324         var o = {};
27325         e.stopEvent();
27326         this.fireEvent("beforeclose", this, o);
27327         if(o.cancel !== true){
27328             this.tabPanel.removeTab(this.id);
27329         }
27330     },
27331     /**
27332      * The text displayed in the tooltip for the close icon.
27333      * @type String
27334      */
27335     closeText : "Close this tab"
27336 });
27337
27338 /** @private */
27339 Roo.TabPanel.prototype.createStrip = function(container){
27340     var strip = document.createElement("div");
27341     strip.className = "x-tabs-wrap";
27342     container.appendChild(strip);
27343     return strip;
27344 };
27345 /** @private */
27346 Roo.TabPanel.prototype.createStripList = function(strip){
27347     // div wrapper for retard IE
27348     // returns the "tr" element.
27349     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27350         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27351         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27352     return strip.firstChild.firstChild.firstChild.firstChild;
27353 };
27354 /** @private */
27355 Roo.TabPanel.prototype.createBody = function(container){
27356     var body = document.createElement("div");
27357     Roo.id(body, "tab-body");
27358     Roo.fly(body).addClass("x-tabs-body");
27359     container.appendChild(body);
27360     return body;
27361 };
27362 /** @private */
27363 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27364     var body = Roo.getDom(id);
27365     if(!body){
27366         body = document.createElement("div");
27367         body.id = id;
27368     }
27369     Roo.fly(body).addClass("x-tabs-item-body");
27370     bodyEl.insertBefore(body, bodyEl.firstChild);
27371     return body;
27372 };
27373 /** @private */
27374 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27375     var td = document.createElement("td");
27376     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27377     //stripEl.appendChild(td);
27378     if(closable){
27379         td.className = "x-tabs-closable";
27380         if(!this.closeTpl){
27381             this.closeTpl = new Roo.Template(
27382                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27383                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27384                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27385             );
27386         }
27387         var el = this.closeTpl.overwrite(td, {"text": text});
27388         var close = el.getElementsByTagName("div")[0];
27389         var inner = el.getElementsByTagName("em")[0];
27390         return {"el": el, "close": close, "inner": inner};
27391     } else {
27392         if(!this.tabTpl){
27393             this.tabTpl = new Roo.Template(
27394                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27395                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27396             );
27397         }
27398         var el = this.tabTpl.overwrite(td, {"text": text});
27399         var inner = el.getElementsByTagName("em")[0];
27400         return {"el": el, "inner": inner};
27401     }
27402 };/*
27403  * Based on:
27404  * Ext JS Library 1.1.1
27405  * Copyright(c) 2006-2007, Ext JS, LLC.
27406  *
27407  * Originally Released Under LGPL - original licence link has changed is not relivant.
27408  *
27409  * Fork - LGPL
27410  * <script type="text/javascript">
27411  */
27412
27413 /**
27414  * @class Roo.Button
27415  * @extends Roo.util.Observable
27416  * Simple Button class
27417  * @cfg {String} text The button text
27418  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27419  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27420  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27421  * @cfg {Object} scope The scope of the handler
27422  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27423  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27424  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27425  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27426  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27427  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27428    applies if enableToggle = true)
27429  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27430  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27431   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27432  * @constructor
27433  * Create a new button
27434  * @param {Object} config The config object
27435  */
27436 Roo.Button = function(renderTo, config)
27437 {
27438     if (!config) {
27439         config = renderTo;
27440         renderTo = config.renderTo || false;
27441     }
27442     
27443     Roo.apply(this, config);
27444     this.addEvents({
27445         /**
27446              * @event click
27447              * Fires when this button is clicked
27448              * @param {Button} this
27449              * @param {EventObject} e The click event
27450              */
27451             "click" : true,
27452         /**
27453              * @event toggle
27454              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27455              * @param {Button} this
27456              * @param {Boolean} pressed
27457              */
27458             "toggle" : true,
27459         /**
27460              * @event mouseover
27461              * Fires when the mouse hovers over the button
27462              * @param {Button} this
27463              * @param {Event} e The event object
27464              */
27465         'mouseover' : true,
27466         /**
27467              * @event mouseout
27468              * Fires when the mouse exits the button
27469              * @param {Button} this
27470              * @param {Event} e The event object
27471              */
27472         'mouseout': true,
27473          /**
27474              * @event render
27475              * Fires when the button is rendered
27476              * @param {Button} this
27477              */
27478         'render': true
27479     });
27480     if(this.menu){
27481         this.menu = Roo.menu.MenuMgr.get(this.menu);
27482     }
27483     // register listeners first!!  - so render can be captured..
27484     Roo.util.Observable.call(this);
27485     if(renderTo){
27486         this.render(renderTo);
27487     }
27488     
27489   
27490 };
27491
27492 Roo.extend(Roo.Button, Roo.util.Observable, {
27493     /**
27494      * 
27495      */
27496     
27497     /**
27498      * Read-only. True if this button is hidden
27499      * @type Boolean
27500      */
27501     hidden : false,
27502     /**
27503      * Read-only. True if this button is disabled
27504      * @type Boolean
27505      */
27506     disabled : false,
27507     /**
27508      * Read-only. True if this button is pressed (only if enableToggle = true)
27509      * @type Boolean
27510      */
27511     pressed : false,
27512
27513     /**
27514      * @cfg {Number} tabIndex 
27515      * The DOM tabIndex for this button (defaults to undefined)
27516      */
27517     tabIndex : undefined,
27518
27519     /**
27520      * @cfg {Boolean} enableToggle
27521      * True to enable pressed/not pressed toggling (defaults to false)
27522      */
27523     enableToggle: false,
27524     /**
27525      * @cfg {Mixed} menu
27526      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27527      */
27528     menu : undefined,
27529     /**
27530      * @cfg {String} menuAlign
27531      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27532      */
27533     menuAlign : "tl-bl?",
27534
27535     /**
27536      * @cfg {String} iconCls
27537      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27538      */
27539     iconCls : undefined,
27540     /**
27541      * @cfg {String} type
27542      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27543      */
27544     type : 'button',
27545
27546     // private
27547     menuClassTarget: 'tr',
27548
27549     /**
27550      * @cfg {String} clickEvent
27551      * The type of event to map to the button's event handler (defaults to 'click')
27552      */
27553     clickEvent : 'click',
27554
27555     /**
27556      * @cfg {Boolean} handleMouseEvents
27557      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27558      */
27559     handleMouseEvents : true,
27560
27561     /**
27562      * @cfg {String} tooltipType
27563      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27564      */
27565     tooltipType : 'qtip',
27566
27567     /**
27568      * @cfg {String} cls
27569      * A CSS class to apply to the button's main element.
27570      */
27571     
27572     /**
27573      * @cfg {Roo.Template} template (Optional)
27574      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27575      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27576      * require code modifications if required elements (e.g. a button) aren't present.
27577      */
27578
27579     // private
27580     render : function(renderTo){
27581         var btn;
27582         if(this.hideParent){
27583             this.parentEl = Roo.get(renderTo);
27584         }
27585         if(!this.dhconfig){
27586             if(!this.template){
27587                 if(!Roo.Button.buttonTemplate){
27588                     // hideous table template
27589                     Roo.Button.buttonTemplate = new Roo.Template(
27590                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27591                         '<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>',
27592                         "</tr></tbody></table>");
27593                 }
27594                 this.template = Roo.Button.buttonTemplate;
27595             }
27596             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27597             var btnEl = btn.child("button:first");
27598             btnEl.on('focus', this.onFocus, this);
27599             btnEl.on('blur', this.onBlur, this);
27600             if(this.cls){
27601                 btn.addClass(this.cls);
27602             }
27603             if(this.icon){
27604                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27605             }
27606             if(this.iconCls){
27607                 btnEl.addClass(this.iconCls);
27608                 if(!this.cls){
27609                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27610                 }
27611             }
27612             if(this.tabIndex !== undefined){
27613                 btnEl.dom.tabIndex = this.tabIndex;
27614             }
27615             if(this.tooltip){
27616                 if(typeof this.tooltip == 'object'){
27617                     Roo.QuickTips.tips(Roo.apply({
27618                           target: btnEl.id
27619                     }, this.tooltip));
27620                 } else {
27621                     btnEl.dom[this.tooltipType] = this.tooltip;
27622                 }
27623             }
27624         }else{
27625             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27626         }
27627         this.el = btn;
27628         if(this.id){
27629             this.el.dom.id = this.el.id = this.id;
27630         }
27631         if(this.menu){
27632             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27633             this.menu.on("show", this.onMenuShow, this);
27634             this.menu.on("hide", this.onMenuHide, this);
27635         }
27636         btn.addClass("x-btn");
27637         if(Roo.isIE && !Roo.isIE7){
27638             this.autoWidth.defer(1, this);
27639         }else{
27640             this.autoWidth();
27641         }
27642         if(this.handleMouseEvents){
27643             btn.on("mouseover", this.onMouseOver, this);
27644             btn.on("mouseout", this.onMouseOut, this);
27645             btn.on("mousedown", this.onMouseDown, this);
27646         }
27647         btn.on(this.clickEvent, this.onClick, this);
27648         //btn.on("mouseup", this.onMouseUp, this);
27649         if(this.hidden){
27650             this.hide();
27651         }
27652         if(this.disabled){
27653             this.disable();
27654         }
27655         Roo.ButtonToggleMgr.register(this);
27656         if(this.pressed){
27657             this.el.addClass("x-btn-pressed");
27658         }
27659         if(this.repeat){
27660             var repeater = new Roo.util.ClickRepeater(btn,
27661                 typeof this.repeat == "object" ? this.repeat : {}
27662             );
27663             repeater.on("click", this.onClick,  this);
27664         }
27665         
27666         this.fireEvent('render', this);
27667         
27668     },
27669     /**
27670      * Returns the button's underlying element
27671      * @return {Roo.Element} The element
27672      */
27673     getEl : function(){
27674         return this.el;  
27675     },
27676     
27677     /**
27678      * Destroys this Button and removes any listeners.
27679      */
27680     destroy : function(){
27681         Roo.ButtonToggleMgr.unregister(this);
27682         this.el.removeAllListeners();
27683         this.purgeListeners();
27684         this.el.remove();
27685     },
27686
27687     // private
27688     autoWidth : function(){
27689         if(this.el){
27690             this.el.setWidth("auto");
27691             if(Roo.isIE7 && Roo.isStrict){
27692                 var ib = this.el.child('button');
27693                 if(ib && ib.getWidth() > 20){
27694                     ib.clip();
27695                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27696                 }
27697             }
27698             if(this.minWidth){
27699                 if(this.hidden){
27700                     this.el.beginMeasure();
27701                 }
27702                 if(this.el.getWidth() < this.minWidth){
27703                     this.el.setWidth(this.minWidth);
27704                 }
27705                 if(this.hidden){
27706                     this.el.endMeasure();
27707                 }
27708             }
27709         }
27710     },
27711
27712     /**
27713      * Assigns this button's click handler
27714      * @param {Function} handler The function to call when the button is clicked
27715      * @param {Object} scope (optional) Scope for the function passed in
27716      */
27717     setHandler : function(handler, scope){
27718         this.handler = handler;
27719         this.scope = scope;  
27720     },
27721     
27722     /**
27723      * Sets this button's text
27724      * @param {String} text The button text
27725      */
27726     setText : function(text){
27727         this.text = text;
27728         if(this.el){
27729             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27730         }
27731         this.autoWidth();
27732     },
27733     
27734     /**
27735      * Gets the text for this button
27736      * @return {String} The button text
27737      */
27738     getText : function(){
27739         return this.text;  
27740     },
27741     
27742     /**
27743      * Show this button
27744      */
27745     show: function(){
27746         this.hidden = false;
27747         if(this.el){
27748             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27749         }
27750     },
27751     
27752     /**
27753      * Hide this button
27754      */
27755     hide: function(){
27756         this.hidden = true;
27757         if(this.el){
27758             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27759         }
27760     },
27761     
27762     /**
27763      * Convenience function for boolean show/hide
27764      * @param {Boolean} visible True to show, false to hide
27765      */
27766     setVisible: function(visible){
27767         if(visible) {
27768             this.show();
27769         }else{
27770             this.hide();
27771         }
27772     },
27773     
27774     /**
27775      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27776      * @param {Boolean} state (optional) Force a particular state
27777      */
27778     toggle : function(state){
27779         state = state === undefined ? !this.pressed : state;
27780         if(state != this.pressed){
27781             if(state){
27782                 this.el.addClass("x-btn-pressed");
27783                 this.pressed = true;
27784                 this.fireEvent("toggle", this, true);
27785             }else{
27786                 this.el.removeClass("x-btn-pressed");
27787                 this.pressed = false;
27788                 this.fireEvent("toggle", this, false);
27789             }
27790             if(this.toggleHandler){
27791                 this.toggleHandler.call(this.scope || this, this, state);
27792             }
27793         }
27794     },
27795     
27796     /**
27797      * Focus the button
27798      */
27799     focus : function(){
27800         this.el.child('button:first').focus();
27801     },
27802     
27803     /**
27804      * Disable this button
27805      */
27806     disable : function(){
27807         if(this.el){
27808             this.el.addClass("x-btn-disabled");
27809         }
27810         this.disabled = true;
27811     },
27812     
27813     /**
27814      * Enable this button
27815      */
27816     enable : function(){
27817         if(this.el){
27818             this.el.removeClass("x-btn-disabled");
27819         }
27820         this.disabled = false;
27821     },
27822
27823     /**
27824      * Convenience function for boolean enable/disable
27825      * @param {Boolean} enabled True to enable, false to disable
27826      */
27827     setDisabled : function(v){
27828         this[v !== true ? "enable" : "disable"]();
27829     },
27830
27831     // private
27832     onClick : function(e){
27833         if(e){
27834             e.preventDefault();
27835         }
27836         if(e.button != 0){
27837             return;
27838         }
27839         if(!this.disabled){
27840             if(this.enableToggle){
27841                 this.toggle();
27842             }
27843             if(this.menu && !this.menu.isVisible()){
27844                 this.menu.show(this.el, this.menuAlign);
27845             }
27846             this.fireEvent("click", this, e);
27847             if(this.handler){
27848                 this.el.removeClass("x-btn-over");
27849                 this.handler.call(this.scope || this, this, e);
27850             }
27851         }
27852     },
27853     // private
27854     onMouseOver : function(e){
27855         if(!this.disabled){
27856             this.el.addClass("x-btn-over");
27857             this.fireEvent('mouseover', this, e);
27858         }
27859     },
27860     // private
27861     onMouseOut : function(e){
27862         if(!e.within(this.el,  true)){
27863             this.el.removeClass("x-btn-over");
27864             this.fireEvent('mouseout', this, e);
27865         }
27866     },
27867     // private
27868     onFocus : function(e){
27869         if(!this.disabled){
27870             this.el.addClass("x-btn-focus");
27871         }
27872     },
27873     // private
27874     onBlur : function(e){
27875         this.el.removeClass("x-btn-focus");
27876     },
27877     // private
27878     onMouseDown : function(e){
27879         if(!this.disabled && e.button == 0){
27880             this.el.addClass("x-btn-click");
27881             Roo.get(document).on('mouseup', this.onMouseUp, this);
27882         }
27883     },
27884     // private
27885     onMouseUp : function(e){
27886         if(e.button == 0){
27887             this.el.removeClass("x-btn-click");
27888             Roo.get(document).un('mouseup', this.onMouseUp, this);
27889         }
27890     },
27891     // private
27892     onMenuShow : function(e){
27893         this.el.addClass("x-btn-menu-active");
27894     },
27895     // private
27896     onMenuHide : function(e){
27897         this.el.removeClass("x-btn-menu-active");
27898     }   
27899 });
27900
27901 // Private utility class used by Button
27902 Roo.ButtonToggleMgr = function(){
27903    var groups = {};
27904    
27905    function toggleGroup(btn, state){
27906        if(state){
27907            var g = groups[btn.toggleGroup];
27908            for(var i = 0, l = g.length; i < l; i++){
27909                if(g[i] != btn){
27910                    g[i].toggle(false);
27911                }
27912            }
27913        }
27914    }
27915    
27916    return {
27917        register : function(btn){
27918            if(!btn.toggleGroup){
27919                return;
27920            }
27921            var g = groups[btn.toggleGroup];
27922            if(!g){
27923                g = groups[btn.toggleGroup] = [];
27924            }
27925            g.push(btn);
27926            btn.on("toggle", toggleGroup);
27927        },
27928        
27929        unregister : function(btn){
27930            if(!btn.toggleGroup){
27931                return;
27932            }
27933            var g = groups[btn.toggleGroup];
27934            if(g){
27935                g.remove(btn);
27936                btn.un("toggle", toggleGroup);
27937            }
27938        }
27939    };
27940 }();/*
27941  * Based on:
27942  * Ext JS Library 1.1.1
27943  * Copyright(c) 2006-2007, Ext JS, LLC.
27944  *
27945  * Originally Released Under LGPL - original licence link has changed is not relivant.
27946  *
27947  * Fork - LGPL
27948  * <script type="text/javascript">
27949  */
27950  
27951 /**
27952  * @class Roo.SplitButton
27953  * @extends Roo.Button
27954  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27955  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27956  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27957  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27958  * @cfg {String} arrowTooltip The title attribute of the arrow
27959  * @constructor
27960  * Create a new menu button
27961  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27962  * @param {Object} config The config object
27963  */
27964 Roo.SplitButton = function(renderTo, config){
27965     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27966     /**
27967      * @event arrowclick
27968      * Fires when this button's arrow is clicked
27969      * @param {SplitButton} this
27970      * @param {EventObject} e The click event
27971      */
27972     this.addEvents({"arrowclick":true});
27973 };
27974
27975 Roo.extend(Roo.SplitButton, Roo.Button, {
27976     render : function(renderTo){
27977         // this is one sweet looking template!
27978         var tpl = new Roo.Template(
27979             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27980             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27981             '<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>',
27982             "</tbody></table></td><td>",
27983             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27984             '<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>',
27985             "</tbody></table></td></tr></table>"
27986         );
27987         var btn = tpl.append(renderTo, [this.text, this.type], true);
27988         var btnEl = btn.child("button");
27989         if(this.cls){
27990             btn.addClass(this.cls);
27991         }
27992         if(this.icon){
27993             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27994         }
27995         if(this.iconCls){
27996             btnEl.addClass(this.iconCls);
27997             if(!this.cls){
27998                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27999             }
28000         }
28001         this.el = btn;
28002         if(this.handleMouseEvents){
28003             btn.on("mouseover", this.onMouseOver, this);
28004             btn.on("mouseout", this.onMouseOut, this);
28005             btn.on("mousedown", this.onMouseDown, this);
28006             btn.on("mouseup", this.onMouseUp, this);
28007         }
28008         btn.on(this.clickEvent, this.onClick, this);
28009         if(this.tooltip){
28010             if(typeof this.tooltip == 'object'){
28011                 Roo.QuickTips.tips(Roo.apply({
28012                       target: btnEl.id
28013                 }, this.tooltip));
28014             } else {
28015                 btnEl.dom[this.tooltipType] = this.tooltip;
28016             }
28017         }
28018         if(this.arrowTooltip){
28019             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28020         }
28021         if(this.hidden){
28022             this.hide();
28023         }
28024         if(this.disabled){
28025             this.disable();
28026         }
28027         if(this.pressed){
28028             this.el.addClass("x-btn-pressed");
28029         }
28030         if(Roo.isIE && !Roo.isIE7){
28031             this.autoWidth.defer(1, this);
28032         }else{
28033             this.autoWidth();
28034         }
28035         if(this.menu){
28036             this.menu.on("show", this.onMenuShow, this);
28037             this.menu.on("hide", this.onMenuHide, this);
28038         }
28039         this.fireEvent('render', this);
28040     },
28041
28042     // private
28043     autoWidth : function(){
28044         if(this.el){
28045             var tbl = this.el.child("table:first");
28046             var tbl2 = this.el.child("table:last");
28047             this.el.setWidth("auto");
28048             tbl.setWidth("auto");
28049             if(Roo.isIE7 && Roo.isStrict){
28050                 var ib = this.el.child('button:first');
28051                 if(ib && ib.getWidth() > 20){
28052                     ib.clip();
28053                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28054                 }
28055             }
28056             if(this.minWidth){
28057                 if(this.hidden){
28058                     this.el.beginMeasure();
28059                 }
28060                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28061                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28062                 }
28063                 if(this.hidden){
28064                     this.el.endMeasure();
28065                 }
28066             }
28067             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28068         } 
28069     },
28070     /**
28071      * Sets this button's click handler
28072      * @param {Function} handler The function to call when the button is clicked
28073      * @param {Object} scope (optional) Scope for the function passed above
28074      */
28075     setHandler : function(handler, scope){
28076         this.handler = handler;
28077         this.scope = scope;  
28078     },
28079     
28080     /**
28081      * Sets this button's arrow click handler
28082      * @param {Function} handler The function to call when the arrow is clicked
28083      * @param {Object} scope (optional) Scope for the function passed above
28084      */
28085     setArrowHandler : function(handler, scope){
28086         this.arrowHandler = handler;
28087         this.scope = scope;  
28088     },
28089     
28090     /**
28091      * Focus the button
28092      */
28093     focus : function(){
28094         if(this.el){
28095             this.el.child("button:first").focus();
28096         }
28097     },
28098
28099     // private
28100     onClick : function(e){
28101         e.preventDefault();
28102         if(!this.disabled){
28103             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28104                 if(this.menu && !this.menu.isVisible()){
28105                     this.menu.show(this.el, this.menuAlign);
28106                 }
28107                 this.fireEvent("arrowclick", this, e);
28108                 if(this.arrowHandler){
28109                     this.arrowHandler.call(this.scope || this, this, e);
28110                 }
28111             }else{
28112                 this.fireEvent("click", this, e);
28113                 if(this.handler){
28114                     this.handler.call(this.scope || this, this, e);
28115                 }
28116             }
28117         }
28118     },
28119     // private
28120     onMouseDown : function(e){
28121         if(!this.disabled){
28122             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28123         }
28124     },
28125     // private
28126     onMouseUp : function(e){
28127         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28128     }   
28129 });
28130
28131
28132 // backwards compat
28133 Roo.MenuButton = Roo.SplitButton;/*
28134  * Based on:
28135  * Ext JS Library 1.1.1
28136  * Copyright(c) 2006-2007, Ext JS, LLC.
28137  *
28138  * Originally Released Under LGPL - original licence link has changed is not relivant.
28139  *
28140  * Fork - LGPL
28141  * <script type="text/javascript">
28142  */
28143
28144 /**
28145  * @class Roo.Toolbar
28146  * Basic Toolbar class.
28147  * @constructor
28148  * Creates a new Toolbar
28149  * @param {Object} container The config object
28150  */ 
28151 Roo.Toolbar = function(container, buttons, config)
28152 {
28153     /// old consturctor format still supported..
28154     if(container instanceof Array){ // omit the container for later rendering
28155         buttons = container;
28156         config = buttons;
28157         container = null;
28158     }
28159     if (typeof(container) == 'object' && container.xtype) {
28160         config = container;
28161         container = config.container;
28162         buttons = config.buttons || []; // not really - use items!!
28163     }
28164     var xitems = [];
28165     if (config && config.items) {
28166         xitems = config.items;
28167         delete config.items;
28168     }
28169     Roo.apply(this, config);
28170     this.buttons = buttons;
28171     
28172     if(container){
28173         this.render(container);
28174     }
28175     this.xitems = xitems;
28176     Roo.each(xitems, function(b) {
28177         this.add(b);
28178     }, this);
28179     
28180 };
28181
28182 Roo.Toolbar.prototype = {
28183     /**
28184      * @cfg {Array} items
28185      * array of button configs or elements to add (will be converted to a MixedCollection)
28186      */
28187     
28188     /**
28189      * @cfg {String/HTMLElement/Element} container
28190      * The id or element that will contain the toolbar
28191      */
28192     // private
28193     render : function(ct){
28194         this.el = Roo.get(ct);
28195         if(this.cls){
28196             this.el.addClass(this.cls);
28197         }
28198         // using a table allows for vertical alignment
28199         // 100% width is needed by Safari...
28200         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28201         this.tr = this.el.child("tr", true);
28202         var autoId = 0;
28203         this.items = new Roo.util.MixedCollection(false, function(o){
28204             return o.id || ("item" + (++autoId));
28205         });
28206         if(this.buttons){
28207             this.add.apply(this, this.buttons);
28208             delete this.buttons;
28209         }
28210     },
28211
28212     /**
28213      * Adds element(s) to the toolbar -- this function takes a variable number of 
28214      * arguments of mixed type and adds them to the toolbar.
28215      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28216      * <ul>
28217      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28218      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28219      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28220      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28221      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28222      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28223      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28224      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28225      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28226      * </ul>
28227      * @param {Mixed} arg2
28228      * @param {Mixed} etc.
28229      */
28230     add : function(){
28231         var a = arguments, l = a.length;
28232         for(var i = 0; i < l; i++){
28233             this._add(a[i]);
28234         }
28235     },
28236     // private..
28237     _add : function(el) {
28238         
28239         if (el.xtype) {
28240             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28241         }
28242         
28243         if (el.applyTo){ // some kind of form field
28244             return this.addField(el);
28245         } 
28246         if (el.render){ // some kind of Toolbar.Item
28247             return this.addItem(el);
28248         }
28249         if (typeof el == "string"){ // string
28250             if(el == "separator" || el == "-"){
28251                 return this.addSeparator();
28252             }
28253             if (el == " "){
28254                 return this.addSpacer();
28255             }
28256             if(el == "->"){
28257                 return this.addFill();
28258             }
28259             return this.addText(el);
28260             
28261         }
28262         if(el.tagName){ // element
28263             return this.addElement(el);
28264         }
28265         if(typeof el == "object"){ // must be button config?
28266             return this.addButton(el);
28267         }
28268         // and now what?!?!
28269         return false;
28270         
28271     },
28272     
28273     /**
28274      * Add an Xtype element
28275      * @param {Object} xtype Xtype Object
28276      * @return {Object} created Object
28277      */
28278     addxtype : function(e){
28279         return this.add(e);  
28280     },
28281     
28282     /**
28283      * Returns the Element for this toolbar.
28284      * @return {Roo.Element}
28285      */
28286     getEl : function(){
28287         return this.el;  
28288     },
28289     
28290     /**
28291      * Adds a separator
28292      * @return {Roo.Toolbar.Item} The separator item
28293      */
28294     addSeparator : function(){
28295         return this.addItem(new Roo.Toolbar.Separator());
28296     },
28297
28298     /**
28299      * Adds a spacer element
28300      * @return {Roo.Toolbar.Spacer} The spacer item
28301      */
28302     addSpacer : function(){
28303         return this.addItem(new Roo.Toolbar.Spacer());
28304     },
28305
28306     /**
28307      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28308      * @return {Roo.Toolbar.Fill} The fill item
28309      */
28310     addFill : function(){
28311         return this.addItem(new Roo.Toolbar.Fill());
28312     },
28313
28314     /**
28315      * Adds any standard HTML element to the toolbar
28316      * @param {String/HTMLElement/Element} el The element or id of the element to add
28317      * @return {Roo.Toolbar.Item} The element's item
28318      */
28319     addElement : function(el){
28320         return this.addItem(new Roo.Toolbar.Item(el));
28321     },
28322     /**
28323      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28324      * @type Roo.util.MixedCollection  
28325      */
28326     items : false,
28327      
28328     /**
28329      * Adds any Toolbar.Item or subclass
28330      * @param {Roo.Toolbar.Item} item
28331      * @return {Roo.Toolbar.Item} The item
28332      */
28333     addItem : function(item){
28334         var td = this.nextBlock();
28335         item.render(td);
28336         this.items.add(item);
28337         return item;
28338     },
28339     
28340     /**
28341      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28342      * @param {Object/Array} config A button config or array of configs
28343      * @return {Roo.Toolbar.Button/Array}
28344      */
28345     addButton : function(config){
28346         if(config instanceof Array){
28347             var buttons = [];
28348             for(var i = 0, len = config.length; i < len; i++) {
28349                 buttons.push(this.addButton(config[i]));
28350             }
28351             return buttons;
28352         }
28353         var b = config;
28354         if(!(config instanceof Roo.Toolbar.Button)){
28355             b = config.split ?
28356                 new Roo.Toolbar.SplitButton(config) :
28357                 new Roo.Toolbar.Button(config);
28358         }
28359         var td = this.nextBlock();
28360         b.render(td);
28361         this.items.add(b);
28362         return b;
28363     },
28364     
28365     /**
28366      * Adds text to the toolbar
28367      * @param {String} text The text to add
28368      * @return {Roo.Toolbar.Item} The element's item
28369      */
28370     addText : function(text){
28371         return this.addItem(new Roo.Toolbar.TextItem(text));
28372     },
28373     
28374     /**
28375      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28376      * @param {Number} index The index where the item is to be inserted
28377      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28378      * @return {Roo.Toolbar.Button/Item}
28379      */
28380     insertButton : function(index, item){
28381         if(item instanceof Array){
28382             var buttons = [];
28383             for(var i = 0, len = item.length; i < len; i++) {
28384                buttons.push(this.insertButton(index + i, item[i]));
28385             }
28386             return buttons;
28387         }
28388         if (!(item instanceof Roo.Toolbar.Button)){
28389            item = new Roo.Toolbar.Button(item);
28390         }
28391         var td = document.createElement("td");
28392         this.tr.insertBefore(td, this.tr.childNodes[index]);
28393         item.render(td);
28394         this.items.insert(index, item);
28395         return item;
28396     },
28397     
28398     /**
28399      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28400      * @param {Object} config
28401      * @return {Roo.Toolbar.Item} The element's item
28402      */
28403     addDom : function(config, returnEl){
28404         var td = this.nextBlock();
28405         Roo.DomHelper.overwrite(td, config);
28406         var ti = new Roo.Toolbar.Item(td.firstChild);
28407         ti.render(td);
28408         this.items.add(ti);
28409         return ti;
28410     },
28411
28412     /**
28413      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28414      * @type Roo.util.MixedCollection  
28415      */
28416     fields : false,
28417     
28418     /**
28419      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28420      * Note: the field should not have been rendered yet. For a field that has already been
28421      * rendered, use {@link #addElement}.
28422      * @param {Roo.form.Field} field
28423      * @return {Roo.ToolbarItem}
28424      */
28425      
28426       
28427     addField : function(field) {
28428         if (!this.fields) {
28429             var autoId = 0;
28430             this.fields = new Roo.util.MixedCollection(false, function(o){
28431                 return o.id || ("item" + (++autoId));
28432             });
28433
28434         }
28435         
28436         var td = this.nextBlock();
28437         field.render(td);
28438         var ti = new Roo.Toolbar.Item(td.firstChild);
28439         ti.render(td);
28440         this.items.add(ti);
28441         this.fields.add(field);
28442         return ti;
28443     },
28444     /**
28445      * Hide the toolbar
28446      * @method hide
28447      */
28448      
28449       
28450     hide : function()
28451     {
28452         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28453         this.el.child('div').hide();
28454     },
28455     /**
28456      * Show the toolbar
28457      * @method show
28458      */
28459     show : function()
28460     {
28461         this.el.child('div').show();
28462     },
28463       
28464     // private
28465     nextBlock : function(){
28466         var td = document.createElement("td");
28467         this.tr.appendChild(td);
28468         return td;
28469     },
28470
28471     // private
28472     destroy : function(){
28473         if(this.items){ // rendered?
28474             Roo.destroy.apply(Roo, this.items.items);
28475         }
28476         if(this.fields){ // rendered?
28477             Roo.destroy.apply(Roo, this.fields.items);
28478         }
28479         Roo.Element.uncache(this.el, this.tr);
28480     }
28481 };
28482
28483 /**
28484  * @class Roo.Toolbar.Item
28485  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28486  * @constructor
28487  * Creates a new Item
28488  * @param {HTMLElement} el 
28489  */
28490 Roo.Toolbar.Item = function(el){
28491     this.el = Roo.getDom(el);
28492     this.id = Roo.id(this.el);
28493     this.hidden = false;
28494 };
28495
28496 Roo.Toolbar.Item.prototype = {
28497     
28498     /**
28499      * Get this item's HTML Element
28500      * @return {HTMLElement}
28501      */
28502     getEl : function(){
28503        return this.el;  
28504     },
28505
28506     // private
28507     render : function(td){
28508         this.td = td;
28509         td.appendChild(this.el);
28510     },
28511     
28512     /**
28513      * Removes and destroys this item.
28514      */
28515     destroy : function(){
28516         this.td.parentNode.removeChild(this.td);
28517     },
28518     
28519     /**
28520      * Shows this item.
28521      */
28522     show: function(){
28523         this.hidden = false;
28524         this.td.style.display = "";
28525     },
28526     
28527     /**
28528      * Hides this item.
28529      */
28530     hide: function(){
28531         this.hidden = true;
28532         this.td.style.display = "none";
28533     },
28534     
28535     /**
28536      * Convenience function for boolean show/hide.
28537      * @param {Boolean} visible true to show/false to hide
28538      */
28539     setVisible: function(visible){
28540         if(visible) {
28541             this.show();
28542         }else{
28543             this.hide();
28544         }
28545     },
28546     
28547     /**
28548      * Try to focus this item.
28549      */
28550     focus : function(){
28551         Roo.fly(this.el).focus();
28552     },
28553     
28554     /**
28555      * Disables this item.
28556      */
28557     disable : function(){
28558         Roo.fly(this.td).addClass("x-item-disabled");
28559         this.disabled = true;
28560         this.el.disabled = true;
28561     },
28562     
28563     /**
28564      * Enables this item.
28565      */
28566     enable : function(){
28567         Roo.fly(this.td).removeClass("x-item-disabled");
28568         this.disabled = false;
28569         this.el.disabled = false;
28570     }
28571 };
28572
28573
28574 /**
28575  * @class Roo.Toolbar.Separator
28576  * @extends Roo.Toolbar.Item
28577  * A simple toolbar separator class
28578  * @constructor
28579  * Creates a new Separator
28580  */
28581 Roo.Toolbar.Separator = function(){
28582     var s = document.createElement("span");
28583     s.className = "ytb-sep";
28584     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28585 };
28586 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28587     enable:Roo.emptyFn,
28588     disable:Roo.emptyFn,
28589     focus:Roo.emptyFn
28590 });
28591
28592 /**
28593  * @class Roo.Toolbar.Spacer
28594  * @extends Roo.Toolbar.Item
28595  * A simple element that adds extra horizontal space to a toolbar.
28596  * @constructor
28597  * Creates a new Spacer
28598  */
28599 Roo.Toolbar.Spacer = function(){
28600     var s = document.createElement("div");
28601     s.className = "ytb-spacer";
28602     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28603 };
28604 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28605     enable:Roo.emptyFn,
28606     disable:Roo.emptyFn,
28607     focus:Roo.emptyFn
28608 });
28609
28610 /**
28611  * @class Roo.Toolbar.Fill
28612  * @extends Roo.Toolbar.Spacer
28613  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28614  * @constructor
28615  * Creates a new Spacer
28616  */
28617 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28618     // private
28619     render : function(td){
28620         td.style.width = '100%';
28621         Roo.Toolbar.Fill.superclass.render.call(this, td);
28622     }
28623 });
28624
28625 /**
28626  * @class Roo.Toolbar.TextItem
28627  * @extends Roo.Toolbar.Item
28628  * A simple class that renders text directly into a toolbar.
28629  * @constructor
28630  * Creates a new TextItem
28631  * @param {String} text
28632  */
28633 Roo.Toolbar.TextItem = function(text){
28634     if (typeof(text) == 'object') {
28635         text = text.text;
28636     }
28637     var s = document.createElement("span");
28638     s.className = "ytb-text";
28639     s.innerHTML = text;
28640     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28641 };
28642 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28643     enable:Roo.emptyFn,
28644     disable:Roo.emptyFn,
28645     focus:Roo.emptyFn
28646 });
28647
28648 /**
28649  * @class Roo.Toolbar.Button
28650  * @extends Roo.Button
28651  * A button that renders into a toolbar.
28652  * @constructor
28653  * Creates a new Button
28654  * @param {Object} config A standard {@link Roo.Button} config object
28655  */
28656 Roo.Toolbar.Button = function(config){
28657     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28658 };
28659 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28660     render : function(td){
28661         this.td = td;
28662         Roo.Toolbar.Button.superclass.render.call(this, td);
28663     },
28664     
28665     /**
28666      * Removes and destroys this button
28667      */
28668     destroy : function(){
28669         Roo.Toolbar.Button.superclass.destroy.call(this);
28670         this.td.parentNode.removeChild(this.td);
28671     },
28672     
28673     /**
28674      * Shows this button
28675      */
28676     show: function(){
28677         this.hidden = false;
28678         this.td.style.display = "";
28679     },
28680     
28681     /**
28682      * Hides this button
28683      */
28684     hide: function(){
28685         this.hidden = true;
28686         this.td.style.display = "none";
28687     },
28688
28689     /**
28690      * Disables this item
28691      */
28692     disable : function(){
28693         Roo.fly(this.td).addClass("x-item-disabled");
28694         this.disabled = true;
28695     },
28696
28697     /**
28698      * Enables this item
28699      */
28700     enable : function(){
28701         Roo.fly(this.td).removeClass("x-item-disabled");
28702         this.disabled = false;
28703     }
28704 });
28705 // backwards compat
28706 Roo.ToolbarButton = Roo.Toolbar.Button;
28707
28708 /**
28709  * @class Roo.Toolbar.SplitButton
28710  * @extends Roo.SplitButton
28711  * A menu button that renders into a toolbar.
28712  * @constructor
28713  * Creates a new SplitButton
28714  * @param {Object} config A standard {@link Roo.SplitButton} config object
28715  */
28716 Roo.Toolbar.SplitButton = function(config){
28717     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28718 };
28719 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28720     render : function(td){
28721         this.td = td;
28722         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28723     },
28724     
28725     /**
28726      * Removes and destroys this button
28727      */
28728     destroy : function(){
28729         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28730         this.td.parentNode.removeChild(this.td);
28731     },
28732     
28733     /**
28734      * Shows this button
28735      */
28736     show: function(){
28737         this.hidden = false;
28738         this.td.style.display = "";
28739     },
28740     
28741     /**
28742      * Hides this button
28743      */
28744     hide: function(){
28745         this.hidden = true;
28746         this.td.style.display = "none";
28747     }
28748 });
28749
28750 // backwards compat
28751 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28752  * Based on:
28753  * Ext JS Library 1.1.1
28754  * Copyright(c) 2006-2007, Ext JS, LLC.
28755  *
28756  * Originally Released Under LGPL - original licence link has changed is not relivant.
28757  *
28758  * Fork - LGPL
28759  * <script type="text/javascript">
28760  */
28761  
28762 /**
28763  * @class Roo.PagingToolbar
28764  * @extends Roo.Toolbar
28765  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28766  * @constructor
28767  * Create a new PagingToolbar
28768  * @param {Object} config The config object
28769  */
28770 Roo.PagingToolbar = function(el, ds, config)
28771 {
28772     // old args format still supported... - xtype is prefered..
28773     if (typeof(el) == 'object' && el.xtype) {
28774         // created from xtype...
28775         config = el;
28776         ds = el.dataSource;
28777         el = config.container;
28778     }
28779     var items = [];
28780     if (config.items) {
28781         items = config.items;
28782         config.items = [];
28783     }
28784     
28785     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28786     this.ds = ds;
28787     this.cursor = 0;
28788     this.renderButtons(this.el);
28789     this.bind(ds);
28790     
28791     // supprot items array.
28792    
28793     Roo.each(items, function(e) {
28794         this.add(Roo.factory(e));
28795     },this);
28796     
28797 };
28798
28799 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28800     /**
28801      * @cfg {Roo.data.Store} dataSource
28802      * The underlying data store providing the paged data
28803      */
28804     /**
28805      * @cfg {String/HTMLElement/Element} container
28806      * container The id or element that will contain the toolbar
28807      */
28808     /**
28809      * @cfg {Boolean} displayInfo
28810      * True to display the displayMsg (defaults to false)
28811      */
28812     /**
28813      * @cfg {Number} pageSize
28814      * The number of records to display per page (defaults to 20)
28815      */
28816     pageSize: 20,
28817     /**
28818      * @cfg {String} displayMsg
28819      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28820      */
28821     displayMsg : 'Displaying {0} - {1} of {2}',
28822     /**
28823      * @cfg {String} emptyMsg
28824      * The message to display when no records are found (defaults to "No data to display")
28825      */
28826     emptyMsg : 'No data to display',
28827     /**
28828      * Customizable piece of the default paging text (defaults to "Page")
28829      * @type String
28830      */
28831     beforePageText : "Page",
28832     /**
28833      * Customizable piece of the default paging text (defaults to "of %0")
28834      * @type String
28835      */
28836     afterPageText : "of {0}",
28837     /**
28838      * Customizable piece of the default paging text (defaults to "First Page")
28839      * @type String
28840      */
28841     firstText : "First Page",
28842     /**
28843      * Customizable piece of the default paging text (defaults to "Previous Page")
28844      * @type String
28845      */
28846     prevText : "Previous Page",
28847     /**
28848      * Customizable piece of the default paging text (defaults to "Next Page")
28849      * @type String
28850      */
28851     nextText : "Next Page",
28852     /**
28853      * Customizable piece of the default paging text (defaults to "Last Page")
28854      * @type String
28855      */
28856     lastText : "Last Page",
28857     /**
28858      * Customizable piece of the default paging text (defaults to "Refresh")
28859      * @type String
28860      */
28861     refreshText : "Refresh",
28862
28863     // private
28864     renderButtons : function(el){
28865         Roo.PagingToolbar.superclass.render.call(this, el);
28866         this.first = this.addButton({
28867             tooltip: this.firstText,
28868             cls: "x-btn-icon x-grid-page-first",
28869             disabled: true,
28870             handler: this.onClick.createDelegate(this, ["first"])
28871         });
28872         this.prev = this.addButton({
28873             tooltip: this.prevText,
28874             cls: "x-btn-icon x-grid-page-prev",
28875             disabled: true,
28876             handler: this.onClick.createDelegate(this, ["prev"])
28877         });
28878         //this.addSeparator();
28879         this.add(this.beforePageText);
28880         this.field = Roo.get(this.addDom({
28881            tag: "input",
28882            type: "text",
28883            size: "3",
28884            value: "1",
28885            cls: "x-grid-page-number"
28886         }).el);
28887         this.field.on("keydown", this.onPagingKeydown, this);
28888         this.field.on("focus", function(){this.dom.select();});
28889         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28890         this.field.setHeight(18);
28891         //this.addSeparator();
28892         this.next = this.addButton({
28893             tooltip: this.nextText,
28894             cls: "x-btn-icon x-grid-page-next",
28895             disabled: true,
28896             handler: this.onClick.createDelegate(this, ["next"])
28897         });
28898         this.last = this.addButton({
28899             tooltip: this.lastText,
28900             cls: "x-btn-icon x-grid-page-last",
28901             disabled: true,
28902             handler: this.onClick.createDelegate(this, ["last"])
28903         });
28904         //this.addSeparator();
28905         this.loading = this.addButton({
28906             tooltip: this.refreshText,
28907             cls: "x-btn-icon x-grid-loading",
28908             handler: this.onClick.createDelegate(this, ["refresh"])
28909         });
28910
28911         if(this.displayInfo){
28912             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28913         }
28914     },
28915
28916     // private
28917     updateInfo : function(){
28918         if(this.displayEl){
28919             var count = this.ds.getCount();
28920             var msg = count == 0 ?
28921                 this.emptyMsg :
28922                 String.format(
28923                     this.displayMsg,
28924                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28925                 );
28926             this.displayEl.update(msg);
28927         }
28928     },
28929
28930     // private
28931     onLoad : function(ds, r, o){
28932        this.cursor = o.params ? o.params.start : 0;
28933        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28934
28935        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28936        this.field.dom.value = ap;
28937        this.first.setDisabled(ap == 1);
28938        this.prev.setDisabled(ap == 1);
28939        this.next.setDisabled(ap == ps);
28940        this.last.setDisabled(ap == ps);
28941        this.loading.enable();
28942        this.updateInfo();
28943     },
28944
28945     // private
28946     getPageData : function(){
28947         var total = this.ds.getTotalCount();
28948         return {
28949             total : total,
28950             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28951             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28952         };
28953     },
28954
28955     // private
28956     onLoadError : function(){
28957         this.loading.enable();
28958     },
28959
28960     // private
28961     onPagingKeydown : function(e){
28962         var k = e.getKey();
28963         var d = this.getPageData();
28964         if(k == e.RETURN){
28965             var v = this.field.dom.value, pageNum;
28966             if(!v || isNaN(pageNum = parseInt(v, 10))){
28967                 this.field.dom.value = d.activePage;
28968                 return;
28969             }
28970             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28971             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28972             e.stopEvent();
28973         }
28974         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))
28975         {
28976           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28977           this.field.dom.value = pageNum;
28978           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28979           e.stopEvent();
28980         }
28981         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28982         {
28983           var v = this.field.dom.value, pageNum; 
28984           var increment = (e.shiftKey) ? 10 : 1;
28985           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28986             increment *= -1;
28987           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28988             this.field.dom.value = d.activePage;
28989             return;
28990           }
28991           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28992           {
28993             this.field.dom.value = parseInt(v, 10) + increment;
28994             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28995             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28996           }
28997           e.stopEvent();
28998         }
28999     },
29000
29001     // private
29002     beforeLoad : function(){
29003         if(this.loading){
29004             this.loading.disable();
29005         }
29006     },
29007
29008     // private
29009     onClick : function(which){
29010         var ds = this.ds;
29011         switch(which){
29012             case "first":
29013                 ds.load({params:{start: 0, limit: this.pageSize}});
29014             break;
29015             case "prev":
29016                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29017             break;
29018             case "next":
29019                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29020             break;
29021             case "last":
29022                 var total = ds.getTotalCount();
29023                 var extra = total % this.pageSize;
29024                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29025                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29026             break;
29027             case "refresh":
29028                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29029             break;
29030         }
29031     },
29032
29033     /**
29034      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29035      * @param {Roo.data.Store} store The data store to unbind
29036      */
29037     unbind : function(ds){
29038         ds.un("beforeload", this.beforeLoad, this);
29039         ds.un("load", this.onLoad, this);
29040         ds.un("loadexception", this.onLoadError, this);
29041         ds.un("remove", this.updateInfo, this);
29042         ds.un("add", this.updateInfo, this);
29043         this.ds = undefined;
29044     },
29045
29046     /**
29047      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29048      * @param {Roo.data.Store} store The data store to bind
29049      */
29050     bind : function(ds){
29051         ds.on("beforeload", this.beforeLoad, this);
29052         ds.on("load", this.onLoad, this);
29053         ds.on("loadexception", this.onLoadError, this);
29054         ds.on("remove", this.updateInfo, this);
29055         ds.on("add", this.updateInfo, this);
29056         this.ds = ds;
29057     }
29058 });/*
29059  * Based on:
29060  * Ext JS Library 1.1.1
29061  * Copyright(c) 2006-2007, Ext JS, LLC.
29062  *
29063  * Originally Released Under LGPL - original licence link has changed is not relivant.
29064  *
29065  * Fork - LGPL
29066  * <script type="text/javascript">
29067  */
29068
29069 /**
29070  * @class Roo.Resizable
29071  * @extends Roo.util.Observable
29072  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29073  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29074  * 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
29075  * the element will be wrapped for you automatically.</p>
29076  * <p>Here is the list of valid resize handles:</p>
29077  * <pre>
29078 Value   Description
29079 ------  -------------------
29080  'n'     north
29081  's'     south
29082  'e'     east
29083  'w'     west
29084  'nw'    northwest
29085  'sw'    southwest
29086  'se'    southeast
29087  'ne'    northeast
29088  'hd'    horizontal drag
29089  'all'   all
29090 </pre>
29091  * <p>Here's an example showing the creation of a typical Resizable:</p>
29092  * <pre><code>
29093 var resizer = new Roo.Resizable("element-id", {
29094     handles: 'all',
29095     minWidth: 200,
29096     minHeight: 100,
29097     maxWidth: 500,
29098     maxHeight: 400,
29099     pinned: true
29100 });
29101 resizer.on("resize", myHandler);
29102 </code></pre>
29103  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29104  * resizer.east.setDisplayed(false);</p>
29105  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29106  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29107  * resize operation's new size (defaults to [0, 0])
29108  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29109  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29110  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29111  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29112  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29113  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29114  * @cfg {Number} width The width of the element in pixels (defaults to null)
29115  * @cfg {Number} height The height of the element in pixels (defaults to null)
29116  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29117  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29118  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29119  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29120  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29121  * in favor of the handles config option (defaults to false)
29122  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29123  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29124  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29125  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29126  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29127  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29128  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29129  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29130  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29131  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29132  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29133  * @constructor
29134  * Create a new resizable component
29135  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29136  * @param {Object} config configuration options
29137   */
29138 Roo.Resizable = function(el, config)
29139 {
29140     this.el = Roo.get(el);
29141
29142     if(config && config.wrap){
29143         config.resizeChild = this.el;
29144         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29145         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29146         this.el.setStyle("overflow", "hidden");
29147         this.el.setPositioning(config.resizeChild.getPositioning());
29148         config.resizeChild.clearPositioning();
29149         if(!config.width || !config.height){
29150             var csize = config.resizeChild.getSize();
29151             this.el.setSize(csize.width, csize.height);
29152         }
29153         if(config.pinned && !config.adjustments){
29154             config.adjustments = "auto";
29155         }
29156     }
29157
29158     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29159     this.proxy.unselectable();
29160     this.proxy.enableDisplayMode('block');
29161
29162     Roo.apply(this, config);
29163
29164     if(this.pinned){
29165         this.disableTrackOver = true;
29166         this.el.addClass("x-resizable-pinned");
29167     }
29168     // if the element isn't positioned, make it relative
29169     var position = this.el.getStyle("position");
29170     if(position != "absolute" && position != "fixed"){
29171         this.el.setStyle("position", "relative");
29172     }
29173     if(!this.handles){ // no handles passed, must be legacy style
29174         this.handles = 's,e,se';
29175         if(this.multiDirectional){
29176             this.handles += ',n,w';
29177         }
29178     }
29179     if(this.handles == "all"){
29180         this.handles = "n s e w ne nw se sw";
29181     }
29182     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29183     var ps = Roo.Resizable.positions;
29184     for(var i = 0, len = hs.length; i < len; i++){
29185         if(hs[i] && ps[hs[i]]){
29186             var pos = ps[hs[i]];
29187             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29188         }
29189     }
29190     // legacy
29191     this.corner = this.southeast;
29192     
29193     // updateBox = the box can move..
29194     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29195         this.updateBox = true;
29196     }
29197
29198     this.activeHandle = null;
29199
29200     if(this.resizeChild){
29201         if(typeof this.resizeChild == "boolean"){
29202             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29203         }else{
29204             this.resizeChild = Roo.get(this.resizeChild, true);
29205         }
29206     }
29207     
29208     if(this.adjustments == "auto"){
29209         var rc = this.resizeChild;
29210         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29211         if(rc && (hw || hn)){
29212             rc.position("relative");
29213             rc.setLeft(hw ? hw.el.getWidth() : 0);
29214             rc.setTop(hn ? hn.el.getHeight() : 0);
29215         }
29216         this.adjustments = [
29217             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29218             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29219         ];
29220     }
29221
29222     if(this.draggable){
29223         this.dd = this.dynamic ?
29224             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29225         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29226     }
29227
29228     // public events
29229     this.addEvents({
29230         /**
29231          * @event beforeresize
29232          * Fired before resize is allowed. Set enabled to false to cancel resize.
29233          * @param {Roo.Resizable} this
29234          * @param {Roo.EventObject} e The mousedown event
29235          */
29236         "beforeresize" : true,
29237         /**
29238          * @event resizing
29239          * Fired a resizing.
29240          * @param {Roo.Resizable} this
29241          * @param {Number} x The new x position
29242          * @param {Number} y The new y position
29243          * @param {Number} w The new w width
29244          * @param {Number} h The new h hight
29245          * @param {Roo.EventObject} e The mouseup event
29246          */
29247         "resizing" : true,
29248         /**
29249          * @event resize
29250          * Fired after a resize.
29251          * @param {Roo.Resizable} this
29252          * @param {Number} width The new width
29253          * @param {Number} height The new height
29254          * @param {Roo.EventObject} e The mouseup event
29255          */
29256         "resize" : true
29257     });
29258
29259     if(this.width !== null && this.height !== null){
29260         this.resizeTo(this.width, this.height);
29261     }else{
29262         this.updateChildSize();
29263     }
29264     if(Roo.isIE){
29265         this.el.dom.style.zoom = 1;
29266     }
29267     Roo.Resizable.superclass.constructor.call(this);
29268 };
29269
29270 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29271         resizeChild : false,
29272         adjustments : [0, 0],
29273         minWidth : 5,
29274         minHeight : 5,
29275         maxWidth : 10000,
29276         maxHeight : 10000,
29277         enabled : true,
29278         animate : false,
29279         duration : .35,
29280         dynamic : false,
29281         handles : false,
29282         multiDirectional : false,
29283         disableTrackOver : false,
29284         easing : 'easeOutStrong',
29285         widthIncrement : 0,
29286         heightIncrement : 0,
29287         pinned : false,
29288         width : null,
29289         height : null,
29290         preserveRatio : false,
29291         transparent: false,
29292         minX: 0,
29293         minY: 0,
29294         draggable: false,
29295
29296         /**
29297          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29298          */
29299         constrainTo: undefined,
29300         /**
29301          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29302          */
29303         resizeRegion: undefined,
29304
29305
29306     /**
29307      * Perform a manual resize
29308      * @param {Number} width
29309      * @param {Number} height
29310      */
29311     resizeTo : function(width, height){
29312         this.el.setSize(width, height);
29313         this.updateChildSize();
29314         this.fireEvent("resize", this, width, height, null);
29315     },
29316
29317     // private
29318     startSizing : function(e, handle){
29319         this.fireEvent("beforeresize", this, e);
29320         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29321
29322             if(!this.overlay){
29323                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29324                 this.overlay.unselectable();
29325                 this.overlay.enableDisplayMode("block");
29326                 this.overlay.on("mousemove", this.onMouseMove, this);
29327                 this.overlay.on("mouseup", this.onMouseUp, this);
29328             }
29329             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29330
29331             this.resizing = true;
29332             this.startBox = this.el.getBox();
29333             this.startPoint = e.getXY();
29334             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29335                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29336
29337             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29338             this.overlay.show();
29339
29340             if(this.constrainTo) {
29341                 var ct = Roo.get(this.constrainTo);
29342                 this.resizeRegion = ct.getRegion().adjust(
29343                     ct.getFrameWidth('t'),
29344                     ct.getFrameWidth('l'),
29345                     -ct.getFrameWidth('b'),
29346                     -ct.getFrameWidth('r')
29347                 );
29348             }
29349
29350             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29351             this.proxy.show();
29352             this.proxy.setBox(this.startBox);
29353             if(!this.dynamic){
29354                 this.proxy.setStyle('visibility', 'visible');
29355             }
29356         }
29357     },
29358
29359     // private
29360     onMouseDown : function(handle, e){
29361         if(this.enabled){
29362             e.stopEvent();
29363             this.activeHandle = handle;
29364             this.startSizing(e, handle);
29365         }
29366     },
29367
29368     // private
29369     onMouseUp : function(e){
29370         var size = this.resizeElement();
29371         this.resizing = false;
29372         this.handleOut();
29373         this.overlay.hide();
29374         this.proxy.hide();
29375         this.fireEvent("resize", this, size.width, size.height, e);
29376     },
29377
29378     // private
29379     updateChildSize : function(){
29380         
29381         if(this.resizeChild){
29382             var el = this.el;
29383             var child = this.resizeChild;
29384             var adj = this.adjustments;
29385             if(el.dom.offsetWidth){
29386                 var b = el.getSize(true);
29387                 child.setSize(b.width+adj[0], b.height+adj[1]);
29388             }
29389             // Second call here for IE
29390             // The first call enables instant resizing and
29391             // the second call corrects scroll bars if they
29392             // exist
29393             if(Roo.isIE){
29394                 setTimeout(function(){
29395                     if(el.dom.offsetWidth){
29396                         var b = el.getSize(true);
29397                         child.setSize(b.width+adj[0], b.height+adj[1]);
29398                     }
29399                 }, 10);
29400             }
29401         }
29402     },
29403
29404     // private
29405     snap : function(value, inc, min){
29406         if(!inc || !value) return value;
29407         var newValue = value;
29408         var m = value % inc;
29409         if(m > 0){
29410             if(m > (inc/2)){
29411                 newValue = value + (inc-m);
29412             }else{
29413                 newValue = value - m;
29414             }
29415         }
29416         return Math.max(min, newValue);
29417     },
29418
29419     // private
29420     resizeElement : function(){
29421         var box = this.proxy.getBox();
29422         if(this.updateBox){
29423             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29424         }else{
29425             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29426         }
29427         this.updateChildSize();
29428         if(!this.dynamic){
29429             this.proxy.hide();
29430         }
29431         return box;
29432     },
29433
29434     // private
29435     constrain : function(v, diff, m, mx){
29436         if(v - diff < m){
29437             diff = v - m;
29438         }else if(v - diff > mx){
29439             diff = mx - v;
29440         }
29441         return diff;
29442     },
29443
29444     // private
29445     onMouseMove : function(e){
29446         
29447         if(this.enabled){
29448             try{// try catch so if something goes wrong the user doesn't get hung
29449
29450             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29451                 return;
29452             }
29453
29454             //var curXY = this.startPoint;
29455             var curSize = this.curSize || this.startBox;
29456             var x = this.startBox.x, y = this.startBox.y;
29457             var ox = x, oy = y;
29458             var w = curSize.width, h = curSize.height;
29459             var ow = w, oh = h;
29460             var mw = this.minWidth, mh = this.minHeight;
29461             var mxw = this.maxWidth, mxh = this.maxHeight;
29462             var wi = this.widthIncrement;
29463             var hi = this.heightIncrement;
29464
29465             var eventXY = e.getXY();
29466             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29467             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29468
29469             var pos = this.activeHandle.position;
29470
29471             switch(pos){
29472                 case "east":
29473                     w += diffX;
29474                     w = Math.min(Math.max(mw, w), mxw);
29475                     break;
29476              
29477                 case "south":
29478                     h += diffY;
29479                     h = Math.min(Math.max(mh, h), mxh);
29480                     break;
29481                 case "southeast":
29482                     w += diffX;
29483                     h += diffY;
29484                     w = Math.min(Math.max(mw, w), mxw);
29485                     h = Math.min(Math.max(mh, h), mxh);
29486                     break;
29487                 case "north":
29488                     diffY = this.constrain(h, diffY, mh, mxh);
29489                     y += diffY;
29490                     h -= diffY;
29491                     break;
29492                 case "hdrag":
29493                     
29494                     if (wi) {
29495                         var adiffX = Math.abs(diffX);
29496                         var sub = (adiffX % wi); // how much 
29497                         if (sub > (wi/2)) { // far enough to snap
29498                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29499                         } else {
29500                             // remove difference.. 
29501                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29502                         }
29503                     }
29504                     x += diffX;
29505                     x = Math.max(this.minX, x);
29506                     break;
29507                 case "west":
29508                     diffX = this.constrain(w, diffX, mw, mxw);
29509                     x += diffX;
29510                     w -= diffX;
29511                     break;
29512                 case "northeast":
29513                     w += diffX;
29514                     w = Math.min(Math.max(mw, w), mxw);
29515                     diffY = this.constrain(h, diffY, mh, mxh);
29516                     y += diffY;
29517                     h -= diffY;
29518                     break;
29519                 case "northwest":
29520                     diffX = this.constrain(w, diffX, mw, mxw);
29521                     diffY = this.constrain(h, diffY, mh, mxh);
29522                     y += diffY;
29523                     h -= diffY;
29524                     x += diffX;
29525                     w -= diffX;
29526                     break;
29527                case "southwest":
29528                     diffX = this.constrain(w, diffX, mw, mxw);
29529                     h += diffY;
29530                     h = Math.min(Math.max(mh, h), mxh);
29531                     x += diffX;
29532                     w -= diffX;
29533                     break;
29534             }
29535
29536             var sw = this.snap(w, wi, mw);
29537             var sh = this.snap(h, hi, mh);
29538             if(sw != w || sh != h){
29539                 switch(pos){
29540                     case "northeast":
29541                         y -= sh - h;
29542                     break;
29543                     case "north":
29544                         y -= sh - h;
29545                         break;
29546                     case "southwest":
29547                         x -= sw - w;
29548                     break;
29549                     case "west":
29550                         x -= sw - w;
29551                         break;
29552                     case "northwest":
29553                         x -= sw - w;
29554                         y -= sh - h;
29555                     break;
29556                 }
29557                 w = sw;
29558                 h = sh;
29559             }
29560
29561             if(this.preserveRatio){
29562                 switch(pos){
29563                     case "southeast":
29564                     case "east":
29565                         h = oh * (w/ow);
29566                         h = Math.min(Math.max(mh, h), mxh);
29567                         w = ow * (h/oh);
29568                        break;
29569                     case "south":
29570                         w = ow * (h/oh);
29571                         w = Math.min(Math.max(mw, w), mxw);
29572                         h = oh * (w/ow);
29573                         break;
29574                     case "northeast":
29575                         w = ow * (h/oh);
29576                         w = Math.min(Math.max(mw, w), mxw);
29577                         h = oh * (w/ow);
29578                     break;
29579                     case "north":
29580                         var tw = w;
29581                         w = ow * (h/oh);
29582                         w = Math.min(Math.max(mw, w), mxw);
29583                         h = oh * (w/ow);
29584                         x += (tw - w) / 2;
29585                         break;
29586                     case "southwest":
29587                         h = oh * (w/ow);
29588                         h = Math.min(Math.max(mh, h), mxh);
29589                         var tw = w;
29590                         w = ow * (h/oh);
29591                         x += tw - w;
29592                         break;
29593                     case "west":
29594                         var th = h;
29595                         h = oh * (w/ow);
29596                         h = Math.min(Math.max(mh, h), mxh);
29597                         y += (th - h) / 2;
29598                         var tw = w;
29599                         w = ow * (h/oh);
29600                         x += tw - w;
29601                        break;
29602                     case "northwest":
29603                         var tw = w;
29604                         var th = h;
29605                         h = oh * (w/ow);
29606                         h = Math.min(Math.max(mh, h), mxh);
29607                         w = ow * (h/oh);
29608                         y += th - h;
29609                         x += tw - w;
29610                        break;
29611
29612                 }
29613             }
29614             if (pos == 'hdrag') {
29615                 w = ow;
29616             }
29617             this.proxy.setBounds(x, y, w, h);
29618             if(this.dynamic){
29619                 this.resizeElement();
29620             }
29621             }catch(e){}
29622         }
29623         this.fireEvent("resizing", this, x, y, w, h, e);
29624     },
29625
29626     // private
29627     handleOver : function(){
29628         if(this.enabled){
29629             this.el.addClass("x-resizable-over");
29630         }
29631     },
29632
29633     // private
29634     handleOut : function(){
29635         if(!this.resizing){
29636             this.el.removeClass("x-resizable-over");
29637         }
29638     },
29639
29640     /**
29641      * Returns the element this component is bound to.
29642      * @return {Roo.Element}
29643      */
29644     getEl : function(){
29645         return this.el;
29646     },
29647
29648     /**
29649      * Returns the resizeChild element (or null).
29650      * @return {Roo.Element}
29651      */
29652     getResizeChild : function(){
29653         return this.resizeChild;
29654     },
29655     groupHandler : function()
29656     {
29657         
29658     },
29659     /**
29660      * Destroys this resizable. If the element was wrapped and
29661      * removeEl is not true then the element remains.
29662      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29663      */
29664     destroy : function(removeEl){
29665         this.proxy.remove();
29666         if(this.overlay){
29667             this.overlay.removeAllListeners();
29668             this.overlay.remove();
29669         }
29670         var ps = Roo.Resizable.positions;
29671         for(var k in ps){
29672             if(typeof ps[k] != "function" && this[ps[k]]){
29673                 var h = this[ps[k]];
29674                 h.el.removeAllListeners();
29675                 h.el.remove();
29676             }
29677         }
29678         if(removeEl){
29679             this.el.update("");
29680             this.el.remove();
29681         }
29682     }
29683 });
29684
29685 // private
29686 // hash to map config positions to true positions
29687 Roo.Resizable.positions = {
29688     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29689     hd: "hdrag"
29690 };
29691
29692 // private
29693 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29694     if(!this.tpl){
29695         // only initialize the template if resizable is used
29696         var tpl = Roo.DomHelper.createTemplate(
29697             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29698         );
29699         tpl.compile();
29700         Roo.Resizable.Handle.prototype.tpl = tpl;
29701     }
29702     this.position = pos;
29703     this.rz = rz;
29704     // show north drag fro topdra
29705     var handlepos = pos == 'hdrag' ? 'north' : pos;
29706     
29707     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29708     if (pos == 'hdrag') {
29709         this.el.setStyle('cursor', 'pointer');
29710     }
29711     this.el.unselectable();
29712     if(transparent){
29713         this.el.setOpacity(0);
29714     }
29715     this.el.on("mousedown", this.onMouseDown, this);
29716     if(!disableTrackOver){
29717         this.el.on("mouseover", this.onMouseOver, this);
29718         this.el.on("mouseout", this.onMouseOut, this);
29719     }
29720 };
29721
29722 // private
29723 Roo.Resizable.Handle.prototype = {
29724     afterResize : function(rz){
29725         Roo.log('after?');
29726         // do nothing
29727     },
29728     // private
29729     onMouseDown : function(e){
29730         this.rz.onMouseDown(this, e);
29731     },
29732     // private
29733     onMouseOver : function(e){
29734         this.rz.handleOver(this, e);
29735     },
29736     // private
29737     onMouseOut : function(e){
29738         this.rz.handleOut(this, e);
29739     }
29740 };/*
29741  * Based on:
29742  * Ext JS Library 1.1.1
29743  * Copyright(c) 2006-2007, Ext JS, LLC.
29744  *
29745  * Originally Released Under LGPL - original licence link has changed is not relivant.
29746  *
29747  * Fork - LGPL
29748  * <script type="text/javascript">
29749  */
29750
29751 /**
29752  * @class Roo.Editor
29753  * @extends Roo.Component
29754  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29755  * @constructor
29756  * Create a new Editor
29757  * @param {Roo.form.Field} field The Field object (or descendant)
29758  * @param {Object} config The config object
29759  */
29760 Roo.Editor = function(field, config){
29761     Roo.Editor.superclass.constructor.call(this, config);
29762     this.field = field;
29763     this.addEvents({
29764         /**
29765              * @event beforestartedit
29766              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29767              * false from the handler of this event.
29768              * @param {Editor} this
29769              * @param {Roo.Element} boundEl The underlying element bound to this editor
29770              * @param {Mixed} value The field value being set
29771              */
29772         "beforestartedit" : true,
29773         /**
29774              * @event startedit
29775              * Fires when this editor is displayed
29776              * @param {Roo.Element} boundEl The underlying element bound to this editor
29777              * @param {Mixed} value The starting field value
29778              */
29779         "startedit" : true,
29780         /**
29781              * @event beforecomplete
29782              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29783              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29784              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29785              * event will not fire since no edit actually occurred.
29786              * @param {Editor} this
29787              * @param {Mixed} value The current field value
29788              * @param {Mixed} startValue The original field value
29789              */
29790         "beforecomplete" : true,
29791         /**
29792              * @event complete
29793              * Fires after editing is complete and any changed value has been written to the underlying field.
29794              * @param {Editor} this
29795              * @param {Mixed} value The current field value
29796              * @param {Mixed} startValue The original field value
29797              */
29798         "complete" : true,
29799         /**
29800          * @event specialkey
29801          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29802          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29803          * @param {Roo.form.Field} this
29804          * @param {Roo.EventObject} e The event object
29805          */
29806         "specialkey" : true
29807     });
29808 };
29809
29810 Roo.extend(Roo.Editor, Roo.Component, {
29811     /**
29812      * @cfg {Boolean/String} autosize
29813      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29814      * or "height" to adopt the height only (defaults to false)
29815      */
29816     /**
29817      * @cfg {Boolean} revertInvalid
29818      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29819      * validation fails (defaults to true)
29820      */
29821     /**
29822      * @cfg {Boolean} ignoreNoChange
29823      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29824      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29825      * will never be ignored.
29826      */
29827     /**
29828      * @cfg {Boolean} hideEl
29829      * False to keep the bound element visible while the editor is displayed (defaults to true)
29830      */
29831     /**
29832      * @cfg {Mixed} value
29833      * The data value of the underlying field (defaults to "")
29834      */
29835     value : "",
29836     /**
29837      * @cfg {String} alignment
29838      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29839      */
29840     alignment: "c-c?",
29841     /**
29842      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29843      * for bottom-right shadow (defaults to "frame")
29844      */
29845     shadow : "frame",
29846     /**
29847      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29848      */
29849     constrain : false,
29850     /**
29851      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29852      */
29853     completeOnEnter : false,
29854     /**
29855      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29856      */
29857     cancelOnEsc : false,
29858     /**
29859      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29860      */
29861     updateEl : false,
29862
29863     // private
29864     onRender : function(ct, position){
29865         this.el = new Roo.Layer({
29866             shadow: this.shadow,
29867             cls: "x-editor",
29868             parentEl : ct,
29869             shim : this.shim,
29870             shadowOffset:4,
29871             id: this.id,
29872             constrain: this.constrain
29873         });
29874         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29875         if(this.field.msgTarget != 'title'){
29876             this.field.msgTarget = 'qtip';
29877         }
29878         this.field.render(this.el);
29879         if(Roo.isGecko){
29880             this.field.el.dom.setAttribute('autocomplete', 'off');
29881         }
29882         this.field.on("specialkey", this.onSpecialKey, this);
29883         if(this.swallowKeys){
29884             this.field.el.swallowEvent(['keydown','keypress']);
29885         }
29886         this.field.show();
29887         this.field.on("blur", this.onBlur, this);
29888         if(this.field.grow){
29889             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29890         }
29891     },
29892
29893     onSpecialKey : function(field, e)
29894     {
29895         //Roo.log('editor onSpecialKey');
29896         if(this.completeOnEnter && e.getKey() == e.ENTER){
29897             e.stopEvent();
29898             this.completeEdit();
29899             return;
29900         }
29901         // do not fire special key otherwise it might hide close the editor...
29902         if(e.getKey() == e.ENTER){    
29903             return;
29904         }
29905         if(this.cancelOnEsc && e.getKey() == e.ESC){
29906             this.cancelEdit();
29907             return;
29908         } 
29909         this.fireEvent('specialkey', field, e);
29910     
29911     },
29912
29913     /**
29914      * Starts the editing process and shows the editor.
29915      * @param {String/HTMLElement/Element} el The element to edit
29916      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29917       * to the innerHTML of el.
29918      */
29919     startEdit : function(el, value){
29920         if(this.editing){
29921             this.completeEdit();
29922         }
29923         this.boundEl = Roo.get(el);
29924         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29925         if(!this.rendered){
29926             this.render(this.parentEl || document.body);
29927         }
29928         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29929             return;
29930         }
29931         this.startValue = v;
29932         this.field.setValue(v);
29933         if(this.autoSize){
29934             var sz = this.boundEl.getSize();
29935             switch(this.autoSize){
29936                 case "width":
29937                 this.setSize(sz.width,  "");
29938                 break;
29939                 case "height":
29940                 this.setSize("",  sz.height);
29941                 break;
29942                 default:
29943                 this.setSize(sz.width,  sz.height);
29944             }
29945         }
29946         this.el.alignTo(this.boundEl, this.alignment);
29947         this.editing = true;
29948         if(Roo.QuickTips){
29949             Roo.QuickTips.disable();
29950         }
29951         this.show();
29952     },
29953
29954     /**
29955      * Sets the height and width of this editor.
29956      * @param {Number} width The new width
29957      * @param {Number} height The new height
29958      */
29959     setSize : function(w, h){
29960         this.field.setSize(w, h);
29961         if(this.el){
29962             this.el.sync();
29963         }
29964     },
29965
29966     /**
29967      * Realigns the editor to the bound field based on the current alignment config value.
29968      */
29969     realign : function(){
29970         this.el.alignTo(this.boundEl, this.alignment);
29971     },
29972
29973     /**
29974      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29975      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29976      */
29977     completeEdit : function(remainVisible){
29978         if(!this.editing){
29979             return;
29980         }
29981         var v = this.getValue();
29982         if(this.revertInvalid !== false && !this.field.isValid()){
29983             v = this.startValue;
29984             this.cancelEdit(true);
29985         }
29986         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29987             this.editing = false;
29988             this.hide();
29989             return;
29990         }
29991         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29992             this.editing = false;
29993             if(this.updateEl && this.boundEl){
29994                 this.boundEl.update(v);
29995             }
29996             if(remainVisible !== true){
29997                 this.hide();
29998             }
29999             this.fireEvent("complete", this, v, this.startValue);
30000         }
30001     },
30002
30003     // private
30004     onShow : function(){
30005         this.el.show();
30006         if(this.hideEl !== false){
30007             this.boundEl.hide();
30008         }
30009         this.field.show();
30010         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30011             this.fixIEFocus = true;
30012             this.deferredFocus.defer(50, this);
30013         }else{
30014             this.field.focus();
30015         }
30016         this.fireEvent("startedit", this.boundEl, this.startValue);
30017     },
30018
30019     deferredFocus : function(){
30020         if(this.editing){
30021             this.field.focus();
30022         }
30023     },
30024
30025     /**
30026      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30027      * reverted to the original starting value.
30028      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30029      * cancel (defaults to false)
30030      */
30031     cancelEdit : function(remainVisible){
30032         if(this.editing){
30033             this.setValue(this.startValue);
30034             if(remainVisible !== true){
30035                 this.hide();
30036             }
30037         }
30038     },
30039
30040     // private
30041     onBlur : function(){
30042         if(this.allowBlur !== true && this.editing){
30043             this.completeEdit();
30044         }
30045     },
30046
30047     // private
30048     onHide : function(){
30049         if(this.editing){
30050             this.completeEdit();
30051             return;
30052         }
30053         this.field.blur();
30054         if(this.field.collapse){
30055             this.field.collapse();
30056         }
30057         this.el.hide();
30058         if(this.hideEl !== false){
30059             this.boundEl.show();
30060         }
30061         if(Roo.QuickTips){
30062             Roo.QuickTips.enable();
30063         }
30064     },
30065
30066     /**
30067      * Sets the data value of the editor
30068      * @param {Mixed} value Any valid value supported by the underlying field
30069      */
30070     setValue : function(v){
30071         this.field.setValue(v);
30072     },
30073
30074     /**
30075      * Gets the data value of the editor
30076      * @return {Mixed} The data value
30077      */
30078     getValue : function(){
30079         return this.field.getValue();
30080     }
30081 });/*
30082  * Based on:
30083  * Ext JS Library 1.1.1
30084  * Copyright(c) 2006-2007, Ext JS, LLC.
30085  *
30086  * Originally Released Under LGPL - original licence link has changed is not relivant.
30087  *
30088  * Fork - LGPL
30089  * <script type="text/javascript">
30090  */
30091  
30092 /**
30093  * @class Roo.BasicDialog
30094  * @extends Roo.util.Observable
30095  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30096  * <pre><code>
30097 var dlg = new Roo.BasicDialog("my-dlg", {
30098     height: 200,
30099     width: 300,
30100     minHeight: 100,
30101     minWidth: 150,
30102     modal: true,
30103     proxyDrag: true,
30104     shadow: true
30105 });
30106 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30107 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30108 dlg.addButton('Cancel', dlg.hide, dlg);
30109 dlg.show();
30110 </code></pre>
30111   <b>A Dialog should always be a direct child of the body element.</b>
30112  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30113  * @cfg {String} title Default text to display in the title bar (defaults to null)
30114  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30115  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30116  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30117  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30118  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30119  * (defaults to null with no animation)
30120  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30121  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30122  * property for valid values (defaults to 'all')
30123  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30124  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30125  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30126  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30127  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30128  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30129  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30130  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30131  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30132  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30133  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30134  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30135  * draggable = true (defaults to false)
30136  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30137  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30138  * shadow (defaults to false)
30139  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30140  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30141  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30142  * @cfg {Array} buttons Array of buttons
30143  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30144  * @constructor
30145  * Create a new BasicDialog.
30146  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30147  * @param {Object} config Configuration options
30148  */
30149 Roo.BasicDialog = function(el, config){
30150     this.el = Roo.get(el);
30151     var dh = Roo.DomHelper;
30152     if(!this.el && config && config.autoCreate){
30153         if(typeof config.autoCreate == "object"){
30154             if(!config.autoCreate.id){
30155                 config.autoCreate.id = el;
30156             }
30157             this.el = dh.append(document.body,
30158                         config.autoCreate, true);
30159         }else{
30160             this.el = dh.append(document.body,
30161                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30162         }
30163     }
30164     el = this.el;
30165     el.setDisplayed(true);
30166     el.hide = this.hideAction;
30167     this.id = el.id;
30168     el.addClass("x-dlg");
30169
30170     Roo.apply(this, config);
30171
30172     this.proxy = el.createProxy("x-dlg-proxy");
30173     this.proxy.hide = this.hideAction;
30174     this.proxy.setOpacity(.5);
30175     this.proxy.hide();
30176
30177     if(config.width){
30178         el.setWidth(config.width);
30179     }
30180     if(config.height){
30181         el.setHeight(config.height);
30182     }
30183     this.size = el.getSize();
30184     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30185         this.xy = [config.x,config.y];
30186     }else{
30187         this.xy = el.getCenterXY(true);
30188     }
30189     /** The header element @type Roo.Element */
30190     this.header = el.child("> .x-dlg-hd");
30191     /** The body element @type Roo.Element */
30192     this.body = el.child("> .x-dlg-bd");
30193     /** The footer element @type Roo.Element */
30194     this.footer = el.child("> .x-dlg-ft");
30195
30196     if(!this.header){
30197         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30198     }
30199     if(!this.body){
30200         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30201     }
30202
30203     this.header.unselectable();
30204     if(this.title){
30205         this.header.update(this.title);
30206     }
30207     // this element allows the dialog to be focused for keyboard event
30208     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30209     this.focusEl.swallowEvent("click", true);
30210
30211     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30212
30213     // wrap the body and footer for special rendering
30214     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30215     if(this.footer){
30216         this.bwrap.dom.appendChild(this.footer.dom);
30217     }
30218
30219     this.bg = this.el.createChild({
30220         tag: "div", cls:"x-dlg-bg",
30221         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30222     });
30223     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30224
30225
30226     if(this.autoScroll !== false && !this.autoTabs){
30227         this.body.setStyle("overflow", "auto");
30228     }
30229
30230     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30231
30232     if(this.closable !== false){
30233         this.el.addClass("x-dlg-closable");
30234         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30235         this.close.on("click", this.closeClick, this);
30236         this.close.addClassOnOver("x-dlg-close-over");
30237     }
30238     if(this.collapsible !== false){
30239         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30240         this.collapseBtn.on("click", this.collapseClick, this);
30241         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30242         this.header.on("dblclick", this.collapseClick, this);
30243     }
30244     if(this.resizable !== false){
30245         this.el.addClass("x-dlg-resizable");
30246         this.resizer = new Roo.Resizable(el, {
30247             minWidth: this.minWidth || 80,
30248             minHeight:this.minHeight || 80,
30249             handles: this.resizeHandles || "all",
30250             pinned: true
30251         });
30252         this.resizer.on("beforeresize", this.beforeResize, this);
30253         this.resizer.on("resize", this.onResize, this);
30254     }
30255     if(this.draggable !== false){
30256         el.addClass("x-dlg-draggable");
30257         if (!this.proxyDrag) {
30258             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30259         }
30260         else {
30261             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30262         }
30263         dd.setHandleElId(this.header.id);
30264         dd.endDrag = this.endMove.createDelegate(this);
30265         dd.startDrag = this.startMove.createDelegate(this);
30266         dd.onDrag = this.onDrag.createDelegate(this);
30267         dd.scroll = false;
30268         this.dd = dd;
30269     }
30270     if(this.modal){
30271         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30272         this.mask.enableDisplayMode("block");
30273         this.mask.hide();
30274         this.el.addClass("x-dlg-modal");
30275     }
30276     if(this.shadow){
30277         this.shadow = new Roo.Shadow({
30278             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30279             offset : this.shadowOffset
30280         });
30281     }else{
30282         this.shadowOffset = 0;
30283     }
30284     if(Roo.useShims && this.shim !== false){
30285         this.shim = this.el.createShim();
30286         this.shim.hide = this.hideAction;
30287         this.shim.hide();
30288     }else{
30289         this.shim = false;
30290     }
30291     if(this.autoTabs){
30292         this.initTabs();
30293     }
30294     if (this.buttons) { 
30295         var bts= this.buttons;
30296         this.buttons = [];
30297         Roo.each(bts, function(b) {
30298             this.addButton(b);
30299         }, this);
30300     }
30301     
30302     
30303     this.addEvents({
30304         /**
30305          * @event keydown
30306          * Fires when a key is pressed
30307          * @param {Roo.BasicDialog} this
30308          * @param {Roo.EventObject} e
30309          */
30310         "keydown" : true,
30311         /**
30312          * @event move
30313          * Fires when this dialog is moved by the user.
30314          * @param {Roo.BasicDialog} this
30315          * @param {Number} x The new page X
30316          * @param {Number} y The new page Y
30317          */
30318         "move" : true,
30319         /**
30320          * @event resize
30321          * Fires when this dialog is resized by the user.
30322          * @param {Roo.BasicDialog} this
30323          * @param {Number} width The new width
30324          * @param {Number} height The new height
30325          */
30326         "resize" : true,
30327         /**
30328          * @event beforehide
30329          * Fires before this dialog is hidden.
30330          * @param {Roo.BasicDialog} this
30331          */
30332         "beforehide" : true,
30333         /**
30334          * @event hide
30335          * Fires when this dialog is hidden.
30336          * @param {Roo.BasicDialog} this
30337          */
30338         "hide" : true,
30339         /**
30340          * @event beforeshow
30341          * Fires before this dialog is shown.
30342          * @param {Roo.BasicDialog} this
30343          */
30344         "beforeshow" : true,
30345         /**
30346          * @event show
30347          * Fires when this dialog is shown.
30348          * @param {Roo.BasicDialog} this
30349          */
30350         "show" : true
30351     });
30352     el.on("keydown", this.onKeyDown, this);
30353     el.on("mousedown", this.toFront, this);
30354     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30355     this.el.hide();
30356     Roo.DialogManager.register(this);
30357     Roo.BasicDialog.superclass.constructor.call(this);
30358 };
30359
30360 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30361     shadowOffset: Roo.isIE ? 6 : 5,
30362     minHeight: 80,
30363     minWidth: 200,
30364     minButtonWidth: 75,
30365     defaultButton: null,
30366     buttonAlign: "right",
30367     tabTag: 'div',
30368     firstShow: true,
30369
30370     /**
30371      * Sets the dialog title text
30372      * @param {String} text The title text to display
30373      * @return {Roo.BasicDialog} this
30374      */
30375     setTitle : function(text){
30376         this.header.update(text);
30377         return this;
30378     },
30379
30380     // private
30381     closeClick : function(){
30382         this.hide();
30383     },
30384
30385     // private
30386     collapseClick : function(){
30387         this[this.collapsed ? "expand" : "collapse"]();
30388     },
30389
30390     /**
30391      * Collapses the dialog to its minimized state (only the title bar is visible).
30392      * Equivalent to the user clicking the collapse dialog button.
30393      */
30394     collapse : function(){
30395         if(!this.collapsed){
30396             this.collapsed = true;
30397             this.el.addClass("x-dlg-collapsed");
30398             this.restoreHeight = this.el.getHeight();
30399             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30400         }
30401     },
30402
30403     /**
30404      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30405      * clicking the expand dialog button.
30406      */
30407     expand : function(){
30408         if(this.collapsed){
30409             this.collapsed = false;
30410             this.el.removeClass("x-dlg-collapsed");
30411             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30412         }
30413     },
30414
30415     /**
30416      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30417      * @return {Roo.TabPanel} The tabs component
30418      */
30419     initTabs : function(){
30420         var tabs = this.getTabs();
30421         while(tabs.getTab(0)){
30422             tabs.removeTab(0);
30423         }
30424         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30425             var dom = el.dom;
30426             tabs.addTab(Roo.id(dom), dom.title);
30427             dom.title = "";
30428         });
30429         tabs.activate(0);
30430         return tabs;
30431     },
30432
30433     // private
30434     beforeResize : function(){
30435         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30436     },
30437
30438     // private
30439     onResize : function(){
30440         this.refreshSize();
30441         this.syncBodyHeight();
30442         this.adjustAssets();
30443         this.focus();
30444         this.fireEvent("resize", this, this.size.width, this.size.height);
30445     },
30446
30447     // private
30448     onKeyDown : function(e){
30449         if(this.isVisible()){
30450             this.fireEvent("keydown", this, e);
30451         }
30452     },
30453
30454     /**
30455      * Resizes the dialog.
30456      * @param {Number} width
30457      * @param {Number} height
30458      * @return {Roo.BasicDialog} this
30459      */
30460     resizeTo : function(width, height){
30461         this.el.setSize(width, height);
30462         this.size = {width: width, height: height};
30463         this.syncBodyHeight();
30464         if(this.fixedcenter){
30465             this.center();
30466         }
30467         if(this.isVisible()){
30468             this.constrainXY();
30469             this.adjustAssets();
30470         }
30471         this.fireEvent("resize", this, width, height);
30472         return this;
30473     },
30474
30475
30476     /**
30477      * Resizes the dialog to fit the specified content size.
30478      * @param {Number} width
30479      * @param {Number} height
30480      * @return {Roo.BasicDialog} this
30481      */
30482     setContentSize : function(w, h){
30483         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30484         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30485         //if(!this.el.isBorderBox()){
30486             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30487             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30488         //}
30489         if(this.tabs){
30490             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30491             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30492         }
30493         this.resizeTo(w, h);
30494         return this;
30495     },
30496
30497     /**
30498      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30499      * executed in response to a particular key being pressed while the dialog is active.
30500      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30501      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30502      * @param {Function} fn The function to call
30503      * @param {Object} scope (optional) The scope of the function
30504      * @return {Roo.BasicDialog} this
30505      */
30506     addKeyListener : function(key, fn, scope){
30507         var keyCode, shift, ctrl, alt;
30508         if(typeof key == "object" && !(key instanceof Array)){
30509             keyCode = key["key"];
30510             shift = key["shift"];
30511             ctrl = key["ctrl"];
30512             alt = key["alt"];
30513         }else{
30514             keyCode = key;
30515         }
30516         var handler = function(dlg, e){
30517             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30518                 var k = e.getKey();
30519                 if(keyCode instanceof Array){
30520                     for(var i = 0, len = keyCode.length; i < len; i++){
30521                         if(keyCode[i] == k){
30522                           fn.call(scope || window, dlg, k, e);
30523                           return;
30524                         }
30525                     }
30526                 }else{
30527                     if(k == keyCode){
30528                         fn.call(scope || window, dlg, k, e);
30529                     }
30530                 }
30531             }
30532         };
30533         this.on("keydown", handler);
30534         return this;
30535     },
30536
30537     /**
30538      * Returns the TabPanel component (creates it if it doesn't exist).
30539      * Note: If you wish to simply check for the existence of tabs without creating them,
30540      * check for a null 'tabs' property.
30541      * @return {Roo.TabPanel} The tabs component
30542      */
30543     getTabs : function(){
30544         if(!this.tabs){
30545             this.el.addClass("x-dlg-auto-tabs");
30546             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30547             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30548         }
30549         return this.tabs;
30550     },
30551
30552     /**
30553      * Adds a button to the footer section of the dialog.
30554      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30555      * object or a valid Roo.DomHelper element config
30556      * @param {Function} handler The function called when the button is clicked
30557      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30558      * @return {Roo.Button} The new button
30559      */
30560     addButton : function(config, handler, scope){
30561         var dh = Roo.DomHelper;
30562         if(!this.footer){
30563             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30564         }
30565         if(!this.btnContainer){
30566             var tb = this.footer.createChild({
30567
30568                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30569                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30570             }, null, true);
30571             this.btnContainer = tb.firstChild.firstChild.firstChild;
30572         }
30573         var bconfig = {
30574             handler: handler,
30575             scope: scope,
30576             minWidth: this.minButtonWidth,
30577             hideParent:true
30578         };
30579         if(typeof config == "string"){
30580             bconfig.text = config;
30581         }else{
30582             if(config.tag){
30583                 bconfig.dhconfig = config;
30584             }else{
30585                 Roo.apply(bconfig, config);
30586             }
30587         }
30588         var fc = false;
30589         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30590             bconfig.position = Math.max(0, bconfig.position);
30591             fc = this.btnContainer.childNodes[bconfig.position];
30592         }
30593          
30594         var btn = new Roo.Button(
30595             fc ? 
30596                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30597                 : this.btnContainer.appendChild(document.createElement("td")),
30598             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30599             bconfig
30600         );
30601         this.syncBodyHeight();
30602         if(!this.buttons){
30603             /**
30604              * Array of all the buttons that have been added to this dialog via addButton
30605              * @type Array
30606              */
30607             this.buttons = [];
30608         }
30609         this.buttons.push(btn);
30610         return btn;
30611     },
30612
30613     /**
30614      * Sets the default button to be focused when the dialog is displayed.
30615      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30616      * @return {Roo.BasicDialog} this
30617      */
30618     setDefaultButton : function(btn){
30619         this.defaultButton = btn;
30620         return this;
30621     },
30622
30623     // private
30624     getHeaderFooterHeight : function(safe){
30625         var height = 0;
30626         if(this.header){
30627            height += this.header.getHeight();
30628         }
30629         if(this.footer){
30630            var fm = this.footer.getMargins();
30631             height += (this.footer.getHeight()+fm.top+fm.bottom);
30632         }
30633         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30634         height += this.centerBg.getPadding("tb");
30635         return height;
30636     },
30637
30638     // private
30639     syncBodyHeight : function()
30640     {
30641         var bd = this.body, // the text
30642             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30643             bw = this.bwrap;
30644         var height = this.size.height - this.getHeaderFooterHeight(false);
30645         bd.setHeight(height-bd.getMargins("tb"));
30646         var hh = this.header.getHeight();
30647         var h = this.size.height-hh;
30648         cb.setHeight(h);
30649         
30650         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30651         bw.setHeight(h-cb.getPadding("tb"));
30652         
30653         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30654         bd.setWidth(bw.getWidth(true));
30655         if(this.tabs){
30656             this.tabs.syncHeight();
30657             if(Roo.isIE){
30658                 this.tabs.el.repaint();
30659             }
30660         }
30661     },
30662
30663     /**
30664      * Restores the previous state of the dialog if Roo.state is configured.
30665      * @return {Roo.BasicDialog} this
30666      */
30667     restoreState : function(){
30668         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30669         if(box && box.width){
30670             this.xy = [box.x, box.y];
30671             this.resizeTo(box.width, box.height);
30672         }
30673         return this;
30674     },
30675
30676     // private
30677     beforeShow : function(){
30678         this.expand();
30679         if(this.fixedcenter){
30680             this.xy = this.el.getCenterXY(true);
30681         }
30682         if(this.modal){
30683             Roo.get(document.body).addClass("x-body-masked");
30684             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30685             this.mask.show();
30686         }
30687         this.constrainXY();
30688     },
30689
30690     // private
30691     animShow : function(){
30692         var b = Roo.get(this.animateTarget).getBox();
30693         this.proxy.setSize(b.width, b.height);
30694         this.proxy.setLocation(b.x, b.y);
30695         this.proxy.show();
30696         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30697                     true, .35, this.showEl.createDelegate(this));
30698     },
30699
30700     /**
30701      * Shows the dialog.
30702      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30703      * @return {Roo.BasicDialog} this
30704      */
30705     show : function(animateTarget){
30706         if (this.fireEvent("beforeshow", this) === false){
30707             return;
30708         }
30709         if(this.syncHeightBeforeShow){
30710             this.syncBodyHeight();
30711         }else if(this.firstShow){
30712             this.firstShow = false;
30713             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30714         }
30715         this.animateTarget = animateTarget || this.animateTarget;
30716         if(!this.el.isVisible()){
30717             this.beforeShow();
30718             if(this.animateTarget && Roo.get(this.animateTarget)){
30719                 this.animShow();
30720             }else{
30721                 this.showEl();
30722             }
30723         }
30724         return this;
30725     },
30726
30727     // private
30728     showEl : function(){
30729         this.proxy.hide();
30730         this.el.setXY(this.xy);
30731         this.el.show();
30732         this.adjustAssets(true);
30733         this.toFront();
30734         this.focus();
30735         // IE peekaboo bug - fix found by Dave Fenwick
30736         if(Roo.isIE){
30737             this.el.repaint();
30738         }
30739         this.fireEvent("show", this);
30740     },
30741
30742     /**
30743      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30744      * dialog itself will receive focus.
30745      */
30746     focus : function(){
30747         if(this.defaultButton){
30748             this.defaultButton.focus();
30749         }else{
30750             this.focusEl.focus();
30751         }
30752     },
30753
30754     // private
30755     constrainXY : function(){
30756         if(this.constraintoviewport !== false){
30757             if(!this.viewSize){
30758                 if(this.container){
30759                     var s = this.container.getSize();
30760                     this.viewSize = [s.width, s.height];
30761                 }else{
30762                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30763                 }
30764             }
30765             var s = Roo.get(this.container||document).getScroll();
30766
30767             var x = this.xy[0], y = this.xy[1];
30768             var w = this.size.width, h = this.size.height;
30769             var vw = this.viewSize[0], vh = this.viewSize[1];
30770             // only move it if it needs it
30771             var moved = false;
30772             // first validate right/bottom
30773             if(x + w > vw+s.left){
30774                 x = vw - w;
30775                 moved = true;
30776             }
30777             if(y + h > vh+s.top){
30778                 y = vh - h;
30779                 moved = true;
30780             }
30781             // then make sure top/left isn't negative
30782             if(x < s.left){
30783                 x = s.left;
30784                 moved = true;
30785             }
30786             if(y < s.top){
30787                 y = s.top;
30788                 moved = true;
30789             }
30790             if(moved){
30791                 // cache xy
30792                 this.xy = [x, y];
30793                 if(this.isVisible()){
30794                     this.el.setLocation(x, y);
30795                     this.adjustAssets();
30796                 }
30797             }
30798         }
30799     },
30800
30801     // private
30802     onDrag : function(){
30803         if(!this.proxyDrag){
30804             this.xy = this.el.getXY();
30805             this.adjustAssets();
30806         }
30807     },
30808
30809     // private
30810     adjustAssets : function(doShow){
30811         var x = this.xy[0], y = this.xy[1];
30812         var w = this.size.width, h = this.size.height;
30813         if(doShow === true){
30814             if(this.shadow){
30815                 this.shadow.show(this.el);
30816             }
30817             if(this.shim){
30818                 this.shim.show();
30819             }
30820         }
30821         if(this.shadow && this.shadow.isVisible()){
30822             this.shadow.show(this.el);
30823         }
30824         if(this.shim && this.shim.isVisible()){
30825             this.shim.setBounds(x, y, w, h);
30826         }
30827     },
30828
30829     // private
30830     adjustViewport : function(w, h){
30831         if(!w || !h){
30832             w = Roo.lib.Dom.getViewWidth();
30833             h = Roo.lib.Dom.getViewHeight();
30834         }
30835         // cache the size
30836         this.viewSize = [w, h];
30837         if(this.modal && this.mask.isVisible()){
30838             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30839             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30840         }
30841         if(this.isVisible()){
30842             this.constrainXY();
30843         }
30844     },
30845
30846     /**
30847      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30848      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30849      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30850      */
30851     destroy : function(removeEl){
30852         if(this.isVisible()){
30853             this.animateTarget = null;
30854             this.hide();
30855         }
30856         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30857         if(this.tabs){
30858             this.tabs.destroy(removeEl);
30859         }
30860         Roo.destroy(
30861              this.shim,
30862              this.proxy,
30863              this.resizer,
30864              this.close,
30865              this.mask
30866         );
30867         if(this.dd){
30868             this.dd.unreg();
30869         }
30870         if(this.buttons){
30871            for(var i = 0, len = this.buttons.length; i < len; i++){
30872                this.buttons[i].destroy();
30873            }
30874         }
30875         this.el.removeAllListeners();
30876         if(removeEl === true){
30877             this.el.update("");
30878             this.el.remove();
30879         }
30880         Roo.DialogManager.unregister(this);
30881     },
30882
30883     // private
30884     startMove : function(){
30885         if(this.proxyDrag){
30886             this.proxy.show();
30887         }
30888         if(this.constraintoviewport !== false){
30889             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30890         }
30891     },
30892
30893     // private
30894     endMove : function(){
30895         if(!this.proxyDrag){
30896             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30897         }else{
30898             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30899             this.proxy.hide();
30900         }
30901         this.refreshSize();
30902         this.adjustAssets();
30903         this.focus();
30904         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30905     },
30906
30907     /**
30908      * Brings this dialog to the front of any other visible dialogs
30909      * @return {Roo.BasicDialog} this
30910      */
30911     toFront : function(){
30912         Roo.DialogManager.bringToFront(this);
30913         return this;
30914     },
30915
30916     /**
30917      * Sends this dialog to the back (under) of any other visible dialogs
30918      * @return {Roo.BasicDialog} this
30919      */
30920     toBack : function(){
30921         Roo.DialogManager.sendToBack(this);
30922         return this;
30923     },
30924
30925     /**
30926      * Centers this dialog in the viewport
30927      * @return {Roo.BasicDialog} this
30928      */
30929     center : function(){
30930         var xy = this.el.getCenterXY(true);
30931         this.moveTo(xy[0], xy[1]);
30932         return this;
30933     },
30934
30935     /**
30936      * Moves the dialog's top-left corner to the specified point
30937      * @param {Number} x
30938      * @param {Number} y
30939      * @return {Roo.BasicDialog} this
30940      */
30941     moveTo : function(x, y){
30942         this.xy = [x,y];
30943         if(this.isVisible()){
30944             this.el.setXY(this.xy);
30945             this.adjustAssets();
30946         }
30947         return this;
30948     },
30949
30950     /**
30951      * Aligns the dialog to the specified element
30952      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30953      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30954      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30955      * @return {Roo.BasicDialog} this
30956      */
30957     alignTo : function(element, position, offsets){
30958         this.xy = this.el.getAlignToXY(element, position, offsets);
30959         if(this.isVisible()){
30960             this.el.setXY(this.xy);
30961             this.adjustAssets();
30962         }
30963         return this;
30964     },
30965
30966     /**
30967      * Anchors an element to another element and realigns it when the window is resized.
30968      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30969      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30970      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30971      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30972      * is a number, it is used as the buffer delay (defaults to 50ms).
30973      * @return {Roo.BasicDialog} this
30974      */
30975     anchorTo : function(el, alignment, offsets, monitorScroll){
30976         var action = function(){
30977             this.alignTo(el, alignment, offsets);
30978         };
30979         Roo.EventManager.onWindowResize(action, this);
30980         var tm = typeof monitorScroll;
30981         if(tm != 'undefined'){
30982             Roo.EventManager.on(window, 'scroll', action, this,
30983                 {buffer: tm == 'number' ? monitorScroll : 50});
30984         }
30985         action.call(this);
30986         return this;
30987     },
30988
30989     /**
30990      * Returns true if the dialog is visible
30991      * @return {Boolean}
30992      */
30993     isVisible : function(){
30994         return this.el.isVisible();
30995     },
30996
30997     // private
30998     animHide : function(callback){
30999         var b = Roo.get(this.animateTarget).getBox();
31000         this.proxy.show();
31001         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31002         this.el.hide();
31003         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31004                     this.hideEl.createDelegate(this, [callback]));
31005     },
31006
31007     /**
31008      * Hides the dialog.
31009      * @param {Function} callback (optional) Function to call when the dialog is hidden
31010      * @return {Roo.BasicDialog} this
31011      */
31012     hide : function(callback){
31013         if (this.fireEvent("beforehide", this) === false){
31014             return;
31015         }
31016         if(this.shadow){
31017             this.shadow.hide();
31018         }
31019         if(this.shim) {
31020           this.shim.hide();
31021         }
31022         // sometimes animateTarget seems to get set.. causing problems...
31023         // this just double checks..
31024         if(this.animateTarget && Roo.get(this.animateTarget)) {
31025            this.animHide(callback);
31026         }else{
31027             this.el.hide();
31028             this.hideEl(callback);
31029         }
31030         return this;
31031     },
31032
31033     // private
31034     hideEl : function(callback){
31035         this.proxy.hide();
31036         if(this.modal){
31037             this.mask.hide();
31038             Roo.get(document.body).removeClass("x-body-masked");
31039         }
31040         this.fireEvent("hide", this);
31041         if(typeof callback == "function"){
31042             callback();
31043         }
31044     },
31045
31046     // private
31047     hideAction : function(){
31048         this.setLeft("-10000px");
31049         this.setTop("-10000px");
31050         this.setStyle("visibility", "hidden");
31051     },
31052
31053     // private
31054     refreshSize : function(){
31055         this.size = this.el.getSize();
31056         this.xy = this.el.getXY();
31057         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31058     },
31059
31060     // private
31061     // z-index is managed by the DialogManager and may be overwritten at any time
31062     setZIndex : function(index){
31063         if(this.modal){
31064             this.mask.setStyle("z-index", index);
31065         }
31066         if(this.shim){
31067             this.shim.setStyle("z-index", ++index);
31068         }
31069         if(this.shadow){
31070             this.shadow.setZIndex(++index);
31071         }
31072         this.el.setStyle("z-index", ++index);
31073         if(this.proxy){
31074             this.proxy.setStyle("z-index", ++index);
31075         }
31076         if(this.resizer){
31077             this.resizer.proxy.setStyle("z-index", ++index);
31078         }
31079
31080         this.lastZIndex = index;
31081     },
31082
31083     /**
31084      * Returns the element for this dialog
31085      * @return {Roo.Element} The underlying dialog Element
31086      */
31087     getEl : function(){
31088         return this.el;
31089     }
31090 });
31091
31092 /**
31093  * @class Roo.DialogManager
31094  * Provides global access to BasicDialogs that have been created and
31095  * support for z-indexing (layering) multiple open dialogs.
31096  */
31097 Roo.DialogManager = function(){
31098     var list = {};
31099     var accessList = [];
31100     var front = null;
31101
31102     // private
31103     var sortDialogs = function(d1, d2){
31104         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31105     };
31106
31107     // private
31108     var orderDialogs = function(){
31109         accessList.sort(sortDialogs);
31110         var seed = Roo.DialogManager.zseed;
31111         for(var i = 0, len = accessList.length; i < len; i++){
31112             var dlg = accessList[i];
31113             if(dlg){
31114                 dlg.setZIndex(seed + (i*10));
31115             }
31116         }
31117     };
31118
31119     return {
31120         /**
31121          * The starting z-index for BasicDialogs (defaults to 9000)
31122          * @type Number The z-index value
31123          */
31124         zseed : 9000,
31125
31126         // private
31127         register : function(dlg){
31128             list[dlg.id] = dlg;
31129             accessList.push(dlg);
31130         },
31131
31132         // private
31133         unregister : function(dlg){
31134             delete list[dlg.id];
31135             var i=0;
31136             var len=0;
31137             if(!accessList.indexOf){
31138                 for(  i = 0, len = accessList.length; i < len; i++){
31139                     if(accessList[i] == dlg){
31140                         accessList.splice(i, 1);
31141                         return;
31142                     }
31143                 }
31144             }else{
31145                  i = accessList.indexOf(dlg);
31146                 if(i != -1){
31147                     accessList.splice(i, 1);
31148                 }
31149             }
31150         },
31151
31152         /**
31153          * Gets a registered dialog by id
31154          * @param {String/Object} id The id of the dialog or a dialog
31155          * @return {Roo.BasicDialog} this
31156          */
31157         get : function(id){
31158             return typeof id == "object" ? id : list[id];
31159         },
31160
31161         /**
31162          * Brings the specified dialog to the front
31163          * @param {String/Object} dlg The id of the dialog or a dialog
31164          * @return {Roo.BasicDialog} this
31165          */
31166         bringToFront : function(dlg){
31167             dlg = this.get(dlg);
31168             if(dlg != front){
31169                 front = dlg;
31170                 dlg._lastAccess = new Date().getTime();
31171                 orderDialogs();
31172             }
31173             return dlg;
31174         },
31175
31176         /**
31177          * Sends the specified dialog to the back
31178          * @param {String/Object} dlg The id of the dialog or a dialog
31179          * @return {Roo.BasicDialog} this
31180          */
31181         sendToBack : function(dlg){
31182             dlg = this.get(dlg);
31183             dlg._lastAccess = -(new Date().getTime());
31184             orderDialogs();
31185             return dlg;
31186         },
31187
31188         /**
31189          * Hides all dialogs
31190          */
31191         hideAll : function(){
31192             for(var id in list){
31193                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31194                     list[id].hide();
31195                 }
31196             }
31197         }
31198     };
31199 }();
31200
31201 /**
31202  * @class Roo.LayoutDialog
31203  * @extends Roo.BasicDialog
31204  * Dialog which provides adjustments for working with a layout in a Dialog.
31205  * Add your necessary layout config options to the dialog's config.<br>
31206  * Example usage (including a nested layout):
31207  * <pre><code>
31208 if(!dialog){
31209     dialog = new Roo.LayoutDialog("download-dlg", {
31210         modal: true,
31211         width:600,
31212         height:450,
31213         shadow:true,
31214         minWidth:500,
31215         minHeight:350,
31216         autoTabs:true,
31217         proxyDrag:true,
31218         // layout config merges with the dialog config
31219         center:{
31220             tabPosition: "top",
31221             alwaysShowTabs: true
31222         }
31223     });
31224     dialog.addKeyListener(27, dialog.hide, dialog);
31225     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31226     dialog.addButton("Build It!", this.getDownload, this);
31227
31228     // we can even add nested layouts
31229     var innerLayout = new Roo.BorderLayout("dl-inner", {
31230         east: {
31231             initialSize: 200,
31232             autoScroll:true,
31233             split:true
31234         },
31235         center: {
31236             autoScroll:true
31237         }
31238     });
31239     innerLayout.beginUpdate();
31240     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31241     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31242     innerLayout.endUpdate(true);
31243
31244     var layout = dialog.getLayout();
31245     layout.beginUpdate();
31246     layout.add("center", new Roo.ContentPanel("standard-panel",
31247                         {title: "Download the Source", fitToFrame:true}));
31248     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31249                {title: "Build your own roo.js"}));
31250     layout.getRegion("center").showPanel(sp);
31251     layout.endUpdate();
31252 }
31253 </code></pre>
31254     * @constructor
31255     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31256     * @param {Object} config configuration options
31257   */
31258 Roo.LayoutDialog = function(el, cfg){
31259     
31260     var config=  cfg;
31261     if (typeof(cfg) == 'undefined') {
31262         config = Roo.apply({}, el);
31263         // not sure why we use documentElement here.. - it should always be body.
31264         // IE7 borks horribly if we use documentElement.
31265         // webkit also does not like documentElement - it creates a body element...
31266         el = Roo.get( document.body || document.documentElement ).createChild();
31267         //config.autoCreate = true;
31268     }
31269     
31270     
31271     config.autoTabs = false;
31272     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31273     this.body.setStyle({overflow:"hidden", position:"relative"});
31274     this.layout = new Roo.BorderLayout(this.body.dom, config);
31275     this.layout.monitorWindowResize = false;
31276     this.el.addClass("x-dlg-auto-layout");
31277     // fix case when center region overwrites center function
31278     this.center = Roo.BasicDialog.prototype.center;
31279     this.on("show", this.layout.layout, this.layout, true);
31280     if (config.items) {
31281         var xitems = config.items;
31282         delete config.items;
31283         Roo.each(xitems, this.addxtype, this);
31284     }
31285     
31286     
31287 };
31288 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31289     /**
31290      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31291      * @deprecated
31292      */
31293     endUpdate : function(){
31294         this.layout.endUpdate();
31295     },
31296
31297     /**
31298      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31299      *  @deprecated
31300      */
31301     beginUpdate : function(){
31302         this.layout.beginUpdate();
31303     },
31304
31305     /**
31306      * Get the BorderLayout for this dialog
31307      * @return {Roo.BorderLayout}
31308      */
31309     getLayout : function(){
31310         return this.layout;
31311     },
31312
31313     showEl : function(){
31314         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31315         if(Roo.isIE7){
31316             this.layout.layout();
31317         }
31318     },
31319
31320     // private
31321     // Use the syncHeightBeforeShow config option to control this automatically
31322     syncBodyHeight : function(){
31323         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31324         if(this.layout){this.layout.layout();}
31325     },
31326     
31327       /**
31328      * Add an xtype element (actually adds to the layout.)
31329      * @return {Object} xdata xtype object data.
31330      */
31331     
31332     addxtype : function(c) {
31333         return this.layout.addxtype(c);
31334     }
31335 });/*
31336  * Based on:
31337  * Ext JS Library 1.1.1
31338  * Copyright(c) 2006-2007, Ext JS, LLC.
31339  *
31340  * Originally Released Under LGPL - original licence link has changed is not relivant.
31341  *
31342  * Fork - LGPL
31343  * <script type="text/javascript">
31344  */
31345  
31346 /**
31347  * @class Roo.MessageBox
31348  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31349  * Example usage:
31350  *<pre><code>
31351 // Basic alert:
31352 Roo.Msg.alert('Status', 'Changes saved successfully.');
31353
31354 // Prompt for user data:
31355 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31356     if (btn == 'ok'){
31357         // process text value...
31358     }
31359 });
31360
31361 // Show a dialog using config options:
31362 Roo.Msg.show({
31363    title:'Save Changes?',
31364    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31365    buttons: Roo.Msg.YESNOCANCEL,
31366    fn: processResult,
31367    animEl: 'elId'
31368 });
31369 </code></pre>
31370  * @singleton
31371  */
31372 Roo.MessageBox = function(){
31373     var dlg, opt, mask, waitTimer;
31374     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31375     var buttons, activeTextEl, bwidth;
31376
31377     // private
31378     var handleButton = function(button){
31379         dlg.hide();
31380         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31381     };
31382
31383     // private
31384     var handleHide = function(){
31385         if(opt && opt.cls){
31386             dlg.el.removeClass(opt.cls);
31387         }
31388         if(waitTimer){
31389             Roo.TaskMgr.stop(waitTimer);
31390             waitTimer = null;
31391         }
31392     };
31393
31394     // private
31395     var updateButtons = function(b){
31396         var width = 0;
31397         if(!b){
31398             buttons["ok"].hide();
31399             buttons["cancel"].hide();
31400             buttons["yes"].hide();
31401             buttons["no"].hide();
31402             dlg.footer.dom.style.display = 'none';
31403             return width;
31404         }
31405         dlg.footer.dom.style.display = '';
31406         for(var k in buttons){
31407             if(typeof buttons[k] != "function"){
31408                 if(b[k]){
31409                     buttons[k].show();
31410                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31411                     width += buttons[k].el.getWidth()+15;
31412                 }else{
31413                     buttons[k].hide();
31414                 }
31415             }
31416         }
31417         return width;
31418     };
31419
31420     // private
31421     var handleEsc = function(d, k, e){
31422         if(opt && opt.closable !== false){
31423             dlg.hide();
31424         }
31425         if(e){
31426             e.stopEvent();
31427         }
31428     };
31429
31430     return {
31431         /**
31432          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31433          * @return {Roo.BasicDialog} The BasicDialog element
31434          */
31435         getDialog : function(){
31436            if(!dlg){
31437                 dlg = new Roo.BasicDialog("x-msg-box", {
31438                     autoCreate : true,
31439                     shadow: true,
31440                     draggable: true,
31441                     resizable:false,
31442                     constraintoviewport:false,
31443                     fixedcenter:true,
31444                     collapsible : false,
31445                     shim:true,
31446                     modal: true,
31447                     width:400, height:100,
31448                     buttonAlign:"center",
31449                     closeClick : function(){
31450                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31451                             handleButton("no");
31452                         }else{
31453                             handleButton("cancel");
31454                         }
31455                     }
31456                 });
31457                 dlg.on("hide", handleHide);
31458                 mask = dlg.mask;
31459                 dlg.addKeyListener(27, handleEsc);
31460                 buttons = {};
31461                 var bt = this.buttonText;
31462                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31463                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31464                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31465                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31466                 bodyEl = dlg.body.createChild({
31467
31468                     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>'
31469                 });
31470                 msgEl = bodyEl.dom.firstChild;
31471                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31472                 textboxEl.enableDisplayMode();
31473                 textboxEl.addKeyListener([10,13], function(){
31474                     if(dlg.isVisible() && opt && opt.buttons){
31475                         if(opt.buttons.ok){
31476                             handleButton("ok");
31477                         }else if(opt.buttons.yes){
31478                             handleButton("yes");
31479                         }
31480                     }
31481                 });
31482                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31483                 textareaEl.enableDisplayMode();
31484                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31485                 progressEl.enableDisplayMode();
31486                 var pf = progressEl.dom.firstChild;
31487                 if (pf) {
31488                     pp = Roo.get(pf.firstChild);
31489                     pp.setHeight(pf.offsetHeight);
31490                 }
31491                 
31492             }
31493             return dlg;
31494         },
31495
31496         /**
31497          * Updates the message box body text
31498          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31499          * the XHTML-compliant non-breaking space character '&amp;#160;')
31500          * @return {Roo.MessageBox} This message box
31501          */
31502         updateText : function(text){
31503             if(!dlg.isVisible() && !opt.width){
31504                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31505             }
31506             msgEl.innerHTML = text || '&#160;';
31507       
31508             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31509             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31510             var w = Math.max(
31511                     Math.min(opt.width || cw , this.maxWidth), 
31512                     Math.max(opt.minWidth || this.minWidth, bwidth)
31513             );
31514             if(opt.prompt){
31515                 activeTextEl.setWidth(w);
31516             }
31517             if(dlg.isVisible()){
31518                 dlg.fixedcenter = false;
31519             }
31520             // to big, make it scroll. = But as usual stupid IE does not support
31521             // !important..
31522             
31523             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31524                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31525                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31526             } else {
31527                 bodyEl.dom.style.height = '';
31528                 bodyEl.dom.style.overflowY = '';
31529             }
31530             if (cw > w) {
31531                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31532             } else {
31533                 bodyEl.dom.style.overflowX = '';
31534             }
31535             
31536             dlg.setContentSize(w, bodyEl.getHeight());
31537             if(dlg.isVisible()){
31538                 dlg.fixedcenter = true;
31539             }
31540             return this;
31541         },
31542
31543         /**
31544          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31545          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31546          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31547          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31548          * @return {Roo.MessageBox} This message box
31549          */
31550         updateProgress : function(value, text){
31551             if(text){
31552                 this.updateText(text);
31553             }
31554             if (pp) { // weird bug on my firefox - for some reason this is not defined
31555                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31556             }
31557             return this;
31558         },        
31559
31560         /**
31561          * Returns true if the message box is currently displayed
31562          * @return {Boolean} True if the message box is visible, else false
31563          */
31564         isVisible : function(){
31565             return dlg && dlg.isVisible();  
31566         },
31567
31568         /**
31569          * Hides the message box if it is displayed
31570          */
31571         hide : function(){
31572             if(this.isVisible()){
31573                 dlg.hide();
31574             }  
31575         },
31576
31577         /**
31578          * Displays a new message box, or reinitializes an existing message box, based on the config options
31579          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31580          * The following config object properties are supported:
31581          * <pre>
31582 Property    Type             Description
31583 ----------  ---------------  ------------------------------------------------------------------------------------
31584 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31585                                    closes (defaults to undefined)
31586 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31587                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31588 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31589                                    progress and wait dialogs will ignore this property and always hide the
31590                                    close button as they can only be closed programmatically.
31591 cls               String           A custom CSS class to apply to the message box element
31592 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31593                                    displayed (defaults to 75)
31594 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31595                                    function will be btn (the name of the button that was clicked, if applicable,
31596                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31597                                    Progress and wait dialogs will ignore this option since they do not respond to
31598                                    user actions and can only be closed programmatically, so any required function
31599                                    should be called by the same code after it closes the dialog.
31600 icon              String           A CSS class that provides a background image to be used as an icon for
31601                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31602 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31603 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31604 modal             Boolean          False to allow user interaction with the page while the message box is
31605                                    displayed (defaults to true)
31606 msg               String           A string that will replace the existing message box body text (defaults
31607                                    to the XHTML-compliant non-breaking space character '&#160;')
31608 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31609 progress          Boolean          True to display a progress bar (defaults to false)
31610 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31611 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31612 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31613 title             String           The title text
31614 value             String           The string value to set into the active textbox element if displayed
31615 wait              Boolean          True to display a progress bar (defaults to false)
31616 width             Number           The width of the dialog in pixels
31617 </pre>
31618          *
31619          * Example usage:
31620          * <pre><code>
31621 Roo.Msg.show({
31622    title: 'Address',
31623    msg: 'Please enter your address:',
31624    width: 300,
31625    buttons: Roo.MessageBox.OKCANCEL,
31626    multiline: true,
31627    fn: saveAddress,
31628    animEl: 'addAddressBtn'
31629 });
31630 </code></pre>
31631          * @param {Object} config Configuration options
31632          * @return {Roo.MessageBox} This message box
31633          */
31634         show : function(options)
31635         {
31636             
31637             // this causes nightmares if you show one dialog after another
31638             // especially on callbacks..
31639              
31640             if(this.isVisible()){
31641                 
31642                 this.hide();
31643                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31644                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31645                 Roo.log("New Dialog Message:" +  options.msg )
31646                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31647                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31648                 
31649             }
31650             var d = this.getDialog();
31651             opt = options;
31652             d.setTitle(opt.title || "&#160;");
31653             d.close.setDisplayed(opt.closable !== false);
31654             activeTextEl = textboxEl;
31655             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31656             if(opt.prompt){
31657                 if(opt.multiline){
31658                     textboxEl.hide();
31659                     textareaEl.show();
31660                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31661                         opt.multiline : this.defaultTextHeight);
31662                     activeTextEl = textareaEl;
31663                 }else{
31664                     textboxEl.show();
31665                     textareaEl.hide();
31666                 }
31667             }else{
31668                 textboxEl.hide();
31669                 textareaEl.hide();
31670             }
31671             progressEl.setDisplayed(opt.progress === true);
31672             this.updateProgress(0);
31673             activeTextEl.dom.value = opt.value || "";
31674             if(opt.prompt){
31675                 dlg.setDefaultButton(activeTextEl);
31676             }else{
31677                 var bs = opt.buttons;
31678                 var db = null;
31679                 if(bs && bs.ok){
31680                     db = buttons["ok"];
31681                 }else if(bs && bs.yes){
31682                     db = buttons["yes"];
31683                 }
31684                 dlg.setDefaultButton(db);
31685             }
31686             bwidth = updateButtons(opt.buttons);
31687             this.updateText(opt.msg);
31688             if(opt.cls){
31689                 d.el.addClass(opt.cls);
31690             }
31691             d.proxyDrag = opt.proxyDrag === true;
31692             d.modal = opt.modal !== false;
31693             d.mask = opt.modal !== false ? mask : false;
31694             if(!d.isVisible()){
31695                 // force it to the end of the z-index stack so it gets a cursor in FF
31696                 document.body.appendChild(dlg.el.dom);
31697                 d.animateTarget = null;
31698                 d.show(options.animEl);
31699             }
31700             return this;
31701         },
31702
31703         /**
31704          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31705          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31706          * and closing the message box when the process is complete.
31707          * @param {String} title The title bar text
31708          * @param {String} msg The message box body text
31709          * @return {Roo.MessageBox} This message box
31710          */
31711         progress : function(title, msg){
31712             this.show({
31713                 title : title,
31714                 msg : msg,
31715                 buttons: false,
31716                 progress:true,
31717                 closable:false,
31718                 minWidth: this.minProgressWidth,
31719                 modal : true
31720             });
31721             return this;
31722         },
31723
31724         /**
31725          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31726          * If a callback function is passed it will be called after the user clicks the button, and the
31727          * id of the button that was clicked will be passed as the only parameter to the callback
31728          * (could also be the top-right close button).
31729          * @param {String} title The title bar text
31730          * @param {String} msg The message box body text
31731          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31732          * @param {Object} scope (optional) The scope of the callback function
31733          * @return {Roo.MessageBox} This message box
31734          */
31735         alert : function(title, msg, fn, scope){
31736             this.show({
31737                 title : title,
31738                 msg : msg,
31739                 buttons: this.OK,
31740                 fn: fn,
31741                 scope : scope,
31742                 modal : true
31743             });
31744             return this;
31745         },
31746
31747         /**
31748          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31749          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31750          * You are responsible for closing the message box when the process is complete.
31751          * @param {String} msg The message box body text
31752          * @param {String} title (optional) The title bar text
31753          * @return {Roo.MessageBox} This message box
31754          */
31755         wait : function(msg, title){
31756             this.show({
31757                 title : title,
31758                 msg : msg,
31759                 buttons: false,
31760                 closable:false,
31761                 progress:true,
31762                 modal:true,
31763                 width:300,
31764                 wait:true
31765             });
31766             waitTimer = Roo.TaskMgr.start({
31767                 run: function(i){
31768                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31769                 },
31770                 interval: 1000
31771             });
31772             return this;
31773         },
31774
31775         /**
31776          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31777          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31778          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31779          * @param {String} title The title bar text
31780          * @param {String} msg The message box body text
31781          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31782          * @param {Object} scope (optional) The scope of the callback function
31783          * @return {Roo.MessageBox} This message box
31784          */
31785         confirm : function(title, msg, fn, scope){
31786             this.show({
31787                 title : title,
31788                 msg : msg,
31789                 buttons: this.YESNO,
31790                 fn: fn,
31791                 scope : scope,
31792                 modal : true
31793             });
31794             return this;
31795         },
31796
31797         /**
31798          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31799          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31800          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31801          * (could also be the top-right close button) and the text that was entered will be passed as the two
31802          * parameters to the callback.
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          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31808          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31809          * @return {Roo.MessageBox} This message box
31810          */
31811         prompt : function(title, msg, fn, scope, multiline){
31812             this.show({
31813                 title : title,
31814                 msg : msg,
31815                 buttons: this.OKCANCEL,
31816                 fn: fn,
31817                 minWidth:250,
31818                 scope : scope,
31819                 prompt:true,
31820                 multiline: multiline,
31821                 modal : true
31822             });
31823             return this;
31824         },
31825
31826         /**
31827          * Button config that displays a single OK button
31828          * @type Object
31829          */
31830         OK : {ok:true},
31831         /**
31832          * Button config that displays Yes and No buttons
31833          * @type Object
31834          */
31835         YESNO : {yes:true, no:true},
31836         /**
31837          * Button config that displays OK and Cancel buttons
31838          * @type Object
31839          */
31840         OKCANCEL : {ok:true, cancel:true},
31841         /**
31842          * Button config that displays Yes, No and Cancel buttons
31843          * @type Object
31844          */
31845         YESNOCANCEL : {yes:true, no:true, cancel:true},
31846
31847         /**
31848          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31849          * @type Number
31850          */
31851         defaultTextHeight : 75,
31852         /**
31853          * The maximum width in pixels of the message box (defaults to 600)
31854          * @type Number
31855          */
31856         maxWidth : 600,
31857         /**
31858          * The minimum width in pixels of the message box (defaults to 100)
31859          * @type Number
31860          */
31861         minWidth : 100,
31862         /**
31863          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31864          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31865          * @type Number
31866          */
31867         minProgressWidth : 250,
31868         /**
31869          * An object containing the default button text strings that can be overriden for localized language support.
31870          * Supported properties are: ok, cancel, yes and no.
31871          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31872          * @type Object
31873          */
31874         buttonText : {
31875             ok : "OK",
31876             cancel : "Cancel",
31877             yes : "Yes",
31878             no : "No"
31879         }
31880     };
31881 }();
31882
31883 /**
31884  * Shorthand for {@link Roo.MessageBox}
31885  */
31886 Roo.Msg = Roo.MessageBox;/*
31887  * Based on:
31888  * Ext JS Library 1.1.1
31889  * Copyright(c) 2006-2007, Ext JS, LLC.
31890  *
31891  * Originally Released Under LGPL - original licence link has changed is not relivant.
31892  *
31893  * Fork - LGPL
31894  * <script type="text/javascript">
31895  */
31896 /**
31897  * @class Roo.QuickTips
31898  * Provides attractive and customizable tooltips for any element.
31899  * @singleton
31900  */
31901 Roo.QuickTips = function(){
31902     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31903     var ce, bd, xy, dd;
31904     var visible = false, disabled = true, inited = false;
31905     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31906     
31907     var onOver = function(e){
31908         if(disabled){
31909             return;
31910         }
31911         var t = e.getTarget();
31912         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31913             return;
31914         }
31915         if(ce && t == ce.el){
31916             clearTimeout(hideProc);
31917             return;
31918         }
31919         if(t && tagEls[t.id]){
31920             tagEls[t.id].el = t;
31921             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31922             return;
31923         }
31924         var ttp, et = Roo.fly(t);
31925         var ns = cfg.namespace;
31926         if(tm.interceptTitles && t.title){
31927             ttp = t.title;
31928             t.qtip = ttp;
31929             t.removeAttribute("title");
31930             e.preventDefault();
31931         }else{
31932             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31933         }
31934         if(ttp){
31935             showProc = show.defer(tm.showDelay, tm, [{
31936                 el: t, 
31937                 text: ttp, 
31938                 width: et.getAttributeNS(ns, cfg.width),
31939                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31940                 title: et.getAttributeNS(ns, cfg.title),
31941                     cls: et.getAttributeNS(ns, cfg.cls)
31942             }]);
31943         }
31944     };
31945     
31946     var onOut = function(e){
31947         clearTimeout(showProc);
31948         var t = e.getTarget();
31949         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31950             hideProc = setTimeout(hide, tm.hideDelay);
31951         }
31952     };
31953     
31954     var onMove = function(e){
31955         if(disabled){
31956             return;
31957         }
31958         xy = e.getXY();
31959         xy[1] += 18;
31960         if(tm.trackMouse && ce){
31961             el.setXY(xy);
31962         }
31963     };
31964     
31965     var onDown = function(e){
31966         clearTimeout(showProc);
31967         clearTimeout(hideProc);
31968         if(!e.within(el)){
31969             if(tm.hideOnClick){
31970                 hide();
31971                 tm.disable();
31972                 tm.enable.defer(100, tm);
31973             }
31974         }
31975     };
31976     
31977     var getPad = function(){
31978         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31979     };
31980
31981     var show = function(o){
31982         if(disabled){
31983             return;
31984         }
31985         clearTimeout(dismissProc);
31986         ce = o;
31987         if(removeCls){ // in case manually hidden
31988             el.removeClass(removeCls);
31989             removeCls = null;
31990         }
31991         if(ce.cls){
31992             el.addClass(ce.cls);
31993             removeCls = ce.cls;
31994         }
31995         if(ce.title){
31996             tipTitle.update(ce.title);
31997             tipTitle.show();
31998         }else{
31999             tipTitle.update('');
32000             tipTitle.hide();
32001         }
32002         el.dom.style.width  = tm.maxWidth+'px';
32003         //tipBody.dom.style.width = '';
32004         tipBodyText.update(o.text);
32005         var p = getPad(), w = ce.width;
32006         if(!w){
32007             var td = tipBodyText.dom;
32008             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32009             if(aw > tm.maxWidth){
32010                 w = tm.maxWidth;
32011             }else if(aw < tm.minWidth){
32012                 w = tm.minWidth;
32013             }else{
32014                 w = aw;
32015             }
32016         }
32017         //tipBody.setWidth(w);
32018         el.setWidth(parseInt(w, 10) + p);
32019         if(ce.autoHide === false){
32020             close.setDisplayed(true);
32021             if(dd){
32022                 dd.unlock();
32023             }
32024         }else{
32025             close.setDisplayed(false);
32026             if(dd){
32027                 dd.lock();
32028             }
32029         }
32030         if(xy){
32031             el.avoidY = xy[1]-18;
32032             el.setXY(xy);
32033         }
32034         if(tm.animate){
32035             el.setOpacity(.1);
32036             el.setStyle("visibility", "visible");
32037             el.fadeIn({callback: afterShow});
32038         }else{
32039             afterShow();
32040         }
32041     };
32042     
32043     var afterShow = function(){
32044         if(ce){
32045             el.show();
32046             esc.enable();
32047             if(tm.autoDismiss && ce.autoHide !== false){
32048                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32049             }
32050         }
32051     };
32052     
32053     var hide = function(noanim){
32054         clearTimeout(dismissProc);
32055         clearTimeout(hideProc);
32056         ce = null;
32057         if(el.isVisible()){
32058             esc.disable();
32059             if(noanim !== true && tm.animate){
32060                 el.fadeOut({callback: afterHide});
32061             }else{
32062                 afterHide();
32063             } 
32064         }
32065     };
32066     
32067     var afterHide = function(){
32068         el.hide();
32069         if(removeCls){
32070             el.removeClass(removeCls);
32071             removeCls = null;
32072         }
32073     };
32074     
32075     return {
32076         /**
32077         * @cfg {Number} minWidth
32078         * The minimum width of the quick tip (defaults to 40)
32079         */
32080        minWidth : 40,
32081         /**
32082         * @cfg {Number} maxWidth
32083         * The maximum width of the quick tip (defaults to 300)
32084         */
32085        maxWidth : 300,
32086         /**
32087         * @cfg {Boolean} interceptTitles
32088         * True to automatically use the element's DOM title value if available (defaults to false)
32089         */
32090        interceptTitles : false,
32091         /**
32092         * @cfg {Boolean} trackMouse
32093         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32094         */
32095        trackMouse : false,
32096         /**
32097         * @cfg {Boolean} hideOnClick
32098         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32099         */
32100        hideOnClick : true,
32101         /**
32102         * @cfg {Number} showDelay
32103         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32104         */
32105        showDelay : 500,
32106         /**
32107         * @cfg {Number} hideDelay
32108         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32109         */
32110        hideDelay : 200,
32111         /**
32112         * @cfg {Boolean} autoHide
32113         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32114         * Used in conjunction with hideDelay.
32115         */
32116        autoHide : true,
32117         /**
32118         * @cfg {Boolean}
32119         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32120         * (defaults to true).  Used in conjunction with autoDismissDelay.
32121         */
32122        autoDismiss : true,
32123         /**
32124         * @cfg {Number}
32125         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32126         */
32127        autoDismissDelay : 5000,
32128        /**
32129         * @cfg {Boolean} animate
32130         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32131         */
32132        animate : false,
32133
32134        /**
32135         * @cfg {String} title
32136         * Title text to display (defaults to '').  This can be any valid HTML markup.
32137         */
32138         title: '',
32139        /**
32140         * @cfg {String} text
32141         * Body text to display (defaults to '').  This can be any valid HTML markup.
32142         */
32143         text : '',
32144        /**
32145         * @cfg {String} cls
32146         * A CSS class to apply to the base quick tip element (defaults to '').
32147         */
32148         cls : '',
32149        /**
32150         * @cfg {Number} width
32151         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32152         * minWidth or maxWidth.
32153         */
32154         width : null,
32155
32156     /**
32157      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32158      * or display QuickTips in a page.
32159      */
32160        init : function(){
32161           tm = Roo.QuickTips;
32162           cfg = tm.tagConfig;
32163           if(!inited){
32164               if(!Roo.isReady){ // allow calling of init() before onReady
32165                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32166                   return;
32167               }
32168               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32169               el.fxDefaults = {stopFx: true};
32170               // maximum custom styling
32171               //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>');
32172               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>');              
32173               tipTitle = el.child('h3');
32174               tipTitle.enableDisplayMode("block");
32175               tipBody = el.child('div.x-tip-bd');
32176               tipBodyText = el.child('div.x-tip-bd-inner');
32177               //bdLeft = el.child('div.x-tip-bd-left');
32178               //bdRight = el.child('div.x-tip-bd-right');
32179               close = el.child('div.x-tip-close');
32180               close.enableDisplayMode("block");
32181               close.on("click", hide);
32182               var d = Roo.get(document);
32183               d.on("mousedown", onDown);
32184               d.on("mouseover", onOver);
32185               d.on("mouseout", onOut);
32186               d.on("mousemove", onMove);
32187               esc = d.addKeyListener(27, hide);
32188               esc.disable();
32189               if(Roo.dd.DD){
32190                   dd = el.initDD("default", null, {
32191                       onDrag : function(){
32192                           el.sync();  
32193                       }
32194                   });
32195                   dd.setHandleElId(tipTitle.id);
32196                   dd.lock();
32197               }
32198               inited = true;
32199           }
32200           this.enable(); 
32201        },
32202
32203     /**
32204      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32205      * are supported:
32206      * <pre>
32207 Property    Type                   Description
32208 ----------  ---------------------  ------------------------------------------------------------------------
32209 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32210      * </ul>
32211      * @param {Object} config The config object
32212      */
32213        register : function(config){
32214            var cs = config instanceof Array ? config : arguments;
32215            for(var i = 0, len = cs.length; i < len; i++) {
32216                var c = cs[i];
32217                var target = c.target;
32218                if(target){
32219                    if(target instanceof Array){
32220                        for(var j = 0, jlen = target.length; j < jlen; j++){
32221                            tagEls[target[j]] = c;
32222                        }
32223                    }else{
32224                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32225                    }
32226                }
32227            }
32228        },
32229
32230     /**
32231      * Removes this quick tip from its element and destroys it.
32232      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32233      */
32234        unregister : function(el){
32235            delete tagEls[Roo.id(el)];
32236        },
32237
32238     /**
32239      * Enable this quick tip.
32240      */
32241        enable : function(){
32242            if(inited && disabled){
32243                locks.pop();
32244                if(locks.length < 1){
32245                    disabled = false;
32246                }
32247            }
32248        },
32249
32250     /**
32251      * Disable this quick tip.
32252      */
32253        disable : function(){
32254           disabled = true;
32255           clearTimeout(showProc);
32256           clearTimeout(hideProc);
32257           clearTimeout(dismissProc);
32258           if(ce){
32259               hide(true);
32260           }
32261           locks.push(1);
32262        },
32263
32264     /**
32265      * Returns true if the quick tip is enabled, else false.
32266      */
32267        isEnabled : function(){
32268             return !disabled;
32269        },
32270
32271         // private
32272        tagConfig : {
32273            namespace : "ext",
32274            attribute : "qtip",
32275            width : "width",
32276            target : "target",
32277            title : "qtitle",
32278            hide : "hide",
32279            cls : "qclass"
32280        }
32281    };
32282 }();
32283
32284 // backwards compat
32285 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32286  * Based on:
32287  * Ext JS Library 1.1.1
32288  * Copyright(c) 2006-2007, Ext JS, LLC.
32289  *
32290  * Originally Released Under LGPL - original licence link has changed is not relivant.
32291  *
32292  * Fork - LGPL
32293  * <script type="text/javascript">
32294  */
32295  
32296
32297 /**
32298  * @class Roo.tree.TreePanel
32299  * @extends Roo.data.Tree
32300
32301  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32302  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32303  * @cfg {Boolean} enableDD true to enable drag and drop
32304  * @cfg {Boolean} enableDrag true to enable just drag
32305  * @cfg {Boolean} enableDrop true to enable just drop
32306  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32307  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32308  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32309  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32310  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32311  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32312  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32313  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32314  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32315  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32316  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32317  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32318  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32319  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32320  * @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>
32321  * @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>
32322  * 
32323  * @constructor
32324  * @param {String/HTMLElement/Element} el The container element
32325  * @param {Object} config
32326  */
32327 Roo.tree.TreePanel = function(el, config){
32328     var root = false;
32329     var loader = false;
32330     if (config.root) {
32331         root = config.root;
32332         delete config.root;
32333     }
32334     if (config.loader) {
32335         loader = config.loader;
32336         delete config.loader;
32337     }
32338     
32339     Roo.apply(this, config);
32340     Roo.tree.TreePanel.superclass.constructor.call(this);
32341     this.el = Roo.get(el);
32342     this.el.addClass('x-tree');
32343     //console.log(root);
32344     if (root) {
32345         this.setRootNode( Roo.factory(root, Roo.tree));
32346     }
32347     if (loader) {
32348         this.loader = Roo.factory(loader, Roo.tree);
32349     }
32350    /**
32351     * Read-only. The id of the container element becomes this TreePanel's id.
32352     */
32353     this.id = this.el.id;
32354     this.addEvents({
32355         /**
32356         * @event beforeload
32357         * Fires before a node is loaded, return false to cancel
32358         * @param {Node} node The node being loaded
32359         */
32360         "beforeload" : true,
32361         /**
32362         * @event load
32363         * Fires when a node is loaded
32364         * @param {Node} node The node that was loaded
32365         */
32366         "load" : true,
32367         /**
32368         * @event textchange
32369         * Fires when the text for a node is changed
32370         * @param {Node} node The node
32371         * @param {String} text The new text
32372         * @param {String} oldText The old text
32373         */
32374         "textchange" : true,
32375         /**
32376         * @event beforeexpand
32377         * Fires before a node is expanded, return false to cancel.
32378         * @param {Node} node The node
32379         * @param {Boolean} deep
32380         * @param {Boolean} anim
32381         */
32382         "beforeexpand" : true,
32383         /**
32384         * @event beforecollapse
32385         * Fires before a node is collapsed, return false to cancel.
32386         * @param {Node} node The node
32387         * @param {Boolean} deep
32388         * @param {Boolean} anim
32389         */
32390         "beforecollapse" : true,
32391         /**
32392         * @event expand
32393         * Fires when a node is expanded
32394         * @param {Node} node The node
32395         */
32396         "expand" : true,
32397         /**
32398         * @event disabledchange
32399         * Fires when the disabled status of a node changes
32400         * @param {Node} node The node
32401         * @param {Boolean} disabled
32402         */
32403         "disabledchange" : true,
32404         /**
32405         * @event collapse
32406         * Fires when a node is collapsed
32407         * @param {Node} node The node
32408         */
32409         "collapse" : true,
32410         /**
32411         * @event beforeclick
32412         * Fires before click processing on a node. Return false to cancel the default action.
32413         * @param {Node} node The node
32414         * @param {Roo.EventObject} e The event object
32415         */
32416         "beforeclick":true,
32417         /**
32418         * @event checkchange
32419         * Fires when a node with a checkbox's checked property changes
32420         * @param {Node} this This node
32421         * @param {Boolean} checked
32422         */
32423         "checkchange":true,
32424         /**
32425         * @event click
32426         * Fires when a node is clicked
32427         * @param {Node} node The node
32428         * @param {Roo.EventObject} e The event object
32429         */
32430         "click":true,
32431         /**
32432         * @event dblclick
32433         * Fires when a node is double clicked
32434         * @param {Node} node The node
32435         * @param {Roo.EventObject} e The event object
32436         */
32437         "dblclick":true,
32438         /**
32439         * @event contextmenu
32440         * Fires when a node is right clicked
32441         * @param {Node} node The node
32442         * @param {Roo.EventObject} e The event object
32443         */
32444         "contextmenu":true,
32445         /**
32446         * @event beforechildrenrendered
32447         * Fires right before the child nodes for a node are rendered
32448         * @param {Node} node The node
32449         */
32450         "beforechildrenrendered":true,
32451         /**
32452         * @event startdrag
32453         * Fires when a node starts being dragged
32454         * @param {Roo.tree.TreePanel} this
32455         * @param {Roo.tree.TreeNode} node
32456         * @param {event} e The raw browser event
32457         */ 
32458        "startdrag" : true,
32459        /**
32460         * @event enddrag
32461         * Fires when a drag operation is complete
32462         * @param {Roo.tree.TreePanel} this
32463         * @param {Roo.tree.TreeNode} node
32464         * @param {event} e The raw browser event
32465         */
32466        "enddrag" : true,
32467        /**
32468         * @event dragdrop
32469         * Fires when a dragged node is dropped on a valid DD target
32470         * @param {Roo.tree.TreePanel} this
32471         * @param {Roo.tree.TreeNode} node
32472         * @param {DD} dd The dd it was dropped on
32473         * @param {event} e The raw browser event
32474         */
32475        "dragdrop" : true,
32476        /**
32477         * @event beforenodedrop
32478         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32479         * passed to handlers has the following properties:<br />
32480         * <ul style="padding:5px;padding-left:16px;">
32481         * <li>tree - The TreePanel</li>
32482         * <li>target - The node being targeted for the drop</li>
32483         * <li>data - The drag data from the drag source</li>
32484         * <li>point - The point of the drop - append, above or below</li>
32485         * <li>source - The drag source</li>
32486         * <li>rawEvent - Raw mouse event</li>
32487         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32488         * to be inserted by setting them on this object.</li>
32489         * <li>cancel - Set this to true to cancel the drop.</li>
32490         * </ul>
32491         * @param {Object} dropEvent
32492         */
32493        "beforenodedrop" : true,
32494        /**
32495         * @event nodedrop
32496         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32497         * passed to handlers has the following properties:<br />
32498         * <ul style="padding:5px;padding-left:16px;">
32499         * <li>tree - The TreePanel</li>
32500         * <li>target - The node being targeted for the drop</li>
32501         * <li>data - The drag data from the drag source</li>
32502         * <li>point - The point of the drop - append, above or below</li>
32503         * <li>source - The drag source</li>
32504         * <li>rawEvent - Raw mouse event</li>
32505         * <li>dropNode - Dropped node(s).</li>
32506         * </ul>
32507         * @param {Object} dropEvent
32508         */
32509        "nodedrop" : true,
32510         /**
32511         * @event nodedragover
32512         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32513         * passed to handlers has the following properties:<br />
32514         * <ul style="padding:5px;padding-left:16px;">
32515         * <li>tree - The TreePanel</li>
32516         * <li>target - The node being targeted for the drop</li>
32517         * <li>data - The drag data from the drag source</li>
32518         * <li>point - The point of the drop - append, above or below</li>
32519         * <li>source - The drag source</li>
32520         * <li>rawEvent - Raw mouse event</li>
32521         * <li>dropNode - Drop node(s) provided by the source.</li>
32522         * <li>cancel - Set this to true to signal drop not allowed.</li>
32523         * </ul>
32524         * @param {Object} dragOverEvent
32525         */
32526        "nodedragover" : true
32527         
32528     });
32529     if(this.singleExpand){
32530        this.on("beforeexpand", this.restrictExpand, this);
32531     }
32532     if (this.editor) {
32533         this.editor.tree = this;
32534         this.editor = Roo.factory(this.editor, Roo.tree);
32535     }
32536     
32537     if (this.selModel) {
32538         this.selModel = Roo.factory(this.selModel, Roo.tree);
32539     }
32540    
32541 };
32542 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32543     rootVisible : true,
32544     animate: Roo.enableFx,
32545     lines : true,
32546     enableDD : false,
32547     hlDrop : Roo.enableFx,
32548   
32549     renderer: false,
32550     
32551     rendererTip: false,
32552     // private
32553     restrictExpand : function(node){
32554         var p = node.parentNode;
32555         if(p){
32556             if(p.expandedChild && p.expandedChild.parentNode == p){
32557                 p.expandedChild.collapse();
32558             }
32559             p.expandedChild = node;
32560         }
32561     },
32562
32563     // private override
32564     setRootNode : function(node){
32565         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32566         if(!this.rootVisible){
32567             node.ui = new Roo.tree.RootTreeNodeUI(node);
32568         }
32569         return node;
32570     },
32571
32572     /**
32573      * Returns the container element for this TreePanel
32574      */
32575     getEl : function(){
32576         return this.el;
32577     },
32578
32579     /**
32580      * Returns the default TreeLoader for this TreePanel
32581      */
32582     getLoader : function(){
32583         return this.loader;
32584     },
32585
32586     /**
32587      * Expand all nodes
32588      */
32589     expandAll : function(){
32590         this.root.expand(true);
32591     },
32592
32593     /**
32594      * Collapse all nodes
32595      */
32596     collapseAll : function(){
32597         this.root.collapse(true);
32598     },
32599
32600     /**
32601      * Returns the selection model used by this TreePanel
32602      */
32603     getSelectionModel : function(){
32604         if(!this.selModel){
32605             this.selModel = new Roo.tree.DefaultSelectionModel();
32606         }
32607         return this.selModel;
32608     },
32609
32610     /**
32611      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32612      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32613      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32614      * @return {Array}
32615      */
32616     getChecked : function(a, startNode){
32617         startNode = startNode || this.root;
32618         var r = [];
32619         var f = function(){
32620             if(this.attributes.checked){
32621                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32622             }
32623         }
32624         startNode.cascade(f);
32625         return r;
32626     },
32627
32628     /**
32629      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32630      * @param {String} path
32631      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32632      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32633      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32634      */
32635     expandPath : function(path, attr, callback){
32636         attr = attr || "id";
32637         var keys = path.split(this.pathSeparator);
32638         var curNode = this.root;
32639         if(curNode.attributes[attr] != keys[1]){ // invalid root
32640             if(callback){
32641                 callback(false, null);
32642             }
32643             return;
32644         }
32645         var index = 1;
32646         var f = function(){
32647             if(++index == keys.length){
32648                 if(callback){
32649                     callback(true, curNode);
32650                 }
32651                 return;
32652             }
32653             var c = curNode.findChild(attr, keys[index]);
32654             if(!c){
32655                 if(callback){
32656                     callback(false, curNode);
32657                 }
32658                 return;
32659             }
32660             curNode = c;
32661             c.expand(false, false, f);
32662         };
32663         curNode.expand(false, false, f);
32664     },
32665
32666     /**
32667      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32668      * @param {String} path
32669      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32670      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32671      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32672      */
32673     selectPath : function(path, attr, callback){
32674         attr = attr || "id";
32675         var keys = path.split(this.pathSeparator);
32676         var v = keys.pop();
32677         if(keys.length > 0){
32678             var f = function(success, node){
32679                 if(success && node){
32680                     var n = node.findChild(attr, v);
32681                     if(n){
32682                         n.select();
32683                         if(callback){
32684                             callback(true, n);
32685                         }
32686                     }else if(callback){
32687                         callback(false, n);
32688                     }
32689                 }else{
32690                     if(callback){
32691                         callback(false, n);
32692                     }
32693                 }
32694             };
32695             this.expandPath(keys.join(this.pathSeparator), attr, f);
32696         }else{
32697             this.root.select();
32698             if(callback){
32699                 callback(true, this.root);
32700             }
32701         }
32702     },
32703
32704     getTreeEl : function(){
32705         return this.el;
32706     },
32707
32708     /**
32709      * Trigger rendering of this TreePanel
32710      */
32711     render : function(){
32712         if (this.innerCt) {
32713             return this; // stop it rendering more than once!!
32714         }
32715         
32716         this.innerCt = this.el.createChild({tag:"ul",
32717                cls:"x-tree-root-ct " +
32718                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32719
32720         if(this.containerScroll){
32721             Roo.dd.ScrollManager.register(this.el);
32722         }
32723         if((this.enableDD || this.enableDrop) && !this.dropZone){
32724            /**
32725             * The dropZone used by this tree if drop is enabled
32726             * @type Roo.tree.TreeDropZone
32727             */
32728              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32729                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32730            });
32731         }
32732         if((this.enableDD || this.enableDrag) && !this.dragZone){
32733            /**
32734             * The dragZone used by this tree if drag is enabled
32735             * @type Roo.tree.TreeDragZone
32736             */
32737             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32738                ddGroup: this.ddGroup || "TreeDD",
32739                scroll: this.ddScroll
32740            });
32741         }
32742         this.getSelectionModel().init(this);
32743         if (!this.root) {
32744             Roo.log("ROOT not set in tree");
32745             return this;
32746         }
32747         this.root.render();
32748         if(!this.rootVisible){
32749             this.root.renderChildren();
32750         }
32751         return this;
32752     }
32753 });/*
32754  * Based on:
32755  * Ext JS Library 1.1.1
32756  * Copyright(c) 2006-2007, Ext JS, LLC.
32757  *
32758  * Originally Released Under LGPL - original licence link has changed is not relivant.
32759  *
32760  * Fork - LGPL
32761  * <script type="text/javascript">
32762  */
32763  
32764
32765 /**
32766  * @class Roo.tree.DefaultSelectionModel
32767  * @extends Roo.util.Observable
32768  * The default single selection for a TreePanel.
32769  * @param {Object} cfg Configuration
32770  */
32771 Roo.tree.DefaultSelectionModel = function(cfg){
32772    this.selNode = null;
32773    
32774    
32775    
32776    this.addEvents({
32777        /**
32778         * @event selectionchange
32779         * Fires when the selected node changes
32780         * @param {DefaultSelectionModel} this
32781         * @param {TreeNode} node the new selection
32782         */
32783        "selectionchange" : true,
32784
32785        /**
32786         * @event beforeselect
32787         * Fires before the selected node changes, return false to cancel the change
32788         * @param {DefaultSelectionModel} this
32789         * @param {TreeNode} node the new selection
32790         * @param {TreeNode} node the old selection
32791         */
32792        "beforeselect" : true
32793    });
32794    
32795     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32796 };
32797
32798 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32799     init : function(tree){
32800         this.tree = tree;
32801         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32802         tree.on("click", this.onNodeClick, this);
32803     },
32804     
32805     onNodeClick : function(node, e){
32806         if (e.ctrlKey && this.selNode == node)  {
32807             this.unselect(node);
32808             return;
32809         }
32810         this.select(node);
32811     },
32812     
32813     /**
32814      * Select a node.
32815      * @param {TreeNode} node The node to select
32816      * @return {TreeNode} The selected node
32817      */
32818     select : function(node){
32819         var last = this.selNode;
32820         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32821             if(last){
32822                 last.ui.onSelectedChange(false);
32823             }
32824             this.selNode = node;
32825             node.ui.onSelectedChange(true);
32826             this.fireEvent("selectionchange", this, node, last);
32827         }
32828         return node;
32829     },
32830     
32831     /**
32832      * Deselect a node.
32833      * @param {TreeNode} node The node to unselect
32834      */
32835     unselect : function(node){
32836         if(this.selNode == node){
32837             this.clearSelections();
32838         }    
32839     },
32840     
32841     /**
32842      * Clear all selections
32843      */
32844     clearSelections : function(){
32845         var n = this.selNode;
32846         if(n){
32847             n.ui.onSelectedChange(false);
32848             this.selNode = null;
32849             this.fireEvent("selectionchange", this, null);
32850         }
32851         return n;
32852     },
32853     
32854     /**
32855      * Get the selected node
32856      * @return {TreeNode} The selected node
32857      */
32858     getSelectedNode : function(){
32859         return this.selNode;    
32860     },
32861     
32862     /**
32863      * Returns true if the node is selected
32864      * @param {TreeNode} node The node to check
32865      * @return {Boolean}
32866      */
32867     isSelected : function(node){
32868         return this.selNode == node;  
32869     },
32870
32871     /**
32872      * Selects the node above the selected node in the tree, intelligently walking the nodes
32873      * @return TreeNode The new selection
32874      */
32875     selectPrevious : function(){
32876         var s = this.selNode || this.lastSelNode;
32877         if(!s){
32878             return null;
32879         }
32880         var ps = s.previousSibling;
32881         if(ps){
32882             if(!ps.isExpanded() || ps.childNodes.length < 1){
32883                 return this.select(ps);
32884             } else{
32885                 var lc = ps.lastChild;
32886                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32887                     lc = lc.lastChild;
32888                 }
32889                 return this.select(lc);
32890             }
32891         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32892             return this.select(s.parentNode);
32893         }
32894         return null;
32895     },
32896
32897     /**
32898      * Selects the node above the selected node in the tree, intelligently walking the nodes
32899      * @return TreeNode The new selection
32900      */
32901     selectNext : function(){
32902         var s = this.selNode || this.lastSelNode;
32903         if(!s){
32904             return null;
32905         }
32906         if(s.firstChild && s.isExpanded()){
32907              return this.select(s.firstChild);
32908          }else if(s.nextSibling){
32909              return this.select(s.nextSibling);
32910          }else if(s.parentNode){
32911             var newS = null;
32912             s.parentNode.bubble(function(){
32913                 if(this.nextSibling){
32914                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32915                     return false;
32916                 }
32917             });
32918             return newS;
32919          }
32920         return null;
32921     },
32922
32923     onKeyDown : function(e){
32924         var s = this.selNode || this.lastSelNode;
32925         // undesirable, but required
32926         var sm = this;
32927         if(!s){
32928             return;
32929         }
32930         var k = e.getKey();
32931         switch(k){
32932              case e.DOWN:
32933                  e.stopEvent();
32934                  this.selectNext();
32935              break;
32936              case e.UP:
32937                  e.stopEvent();
32938                  this.selectPrevious();
32939              break;
32940              case e.RIGHT:
32941                  e.preventDefault();
32942                  if(s.hasChildNodes()){
32943                      if(!s.isExpanded()){
32944                          s.expand();
32945                      }else if(s.firstChild){
32946                          this.select(s.firstChild, e);
32947                      }
32948                  }
32949              break;
32950              case e.LEFT:
32951                  e.preventDefault();
32952                  if(s.hasChildNodes() && s.isExpanded()){
32953                      s.collapse();
32954                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32955                      this.select(s.parentNode, e);
32956                  }
32957              break;
32958         };
32959     }
32960 });
32961
32962 /**
32963  * @class Roo.tree.MultiSelectionModel
32964  * @extends Roo.util.Observable
32965  * Multi selection for a TreePanel.
32966  * @param {Object} cfg Configuration
32967  */
32968 Roo.tree.MultiSelectionModel = function(){
32969    this.selNodes = [];
32970    this.selMap = {};
32971    this.addEvents({
32972        /**
32973         * @event selectionchange
32974         * Fires when the selected nodes change
32975         * @param {MultiSelectionModel} this
32976         * @param {Array} nodes Array of the selected nodes
32977         */
32978        "selectionchange" : true
32979    });
32980    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32981    
32982 };
32983
32984 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32985     init : function(tree){
32986         this.tree = tree;
32987         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32988         tree.on("click", this.onNodeClick, this);
32989     },
32990     
32991     onNodeClick : function(node, e){
32992         this.select(node, e, e.ctrlKey);
32993     },
32994     
32995     /**
32996      * Select a node.
32997      * @param {TreeNode} node The node to select
32998      * @param {EventObject} e (optional) An event associated with the selection
32999      * @param {Boolean} keepExisting True to retain existing selections
33000      * @return {TreeNode} The selected node
33001      */
33002     select : function(node, e, keepExisting){
33003         if(keepExisting !== true){
33004             this.clearSelections(true);
33005         }
33006         if(this.isSelected(node)){
33007             this.lastSelNode = node;
33008             return node;
33009         }
33010         this.selNodes.push(node);
33011         this.selMap[node.id] = node;
33012         this.lastSelNode = node;
33013         node.ui.onSelectedChange(true);
33014         this.fireEvent("selectionchange", this, this.selNodes);
33015         return node;
33016     },
33017     
33018     /**
33019      * Deselect a node.
33020      * @param {TreeNode} node The node to unselect
33021      */
33022     unselect : function(node){
33023         if(this.selMap[node.id]){
33024             node.ui.onSelectedChange(false);
33025             var sn = this.selNodes;
33026             var index = -1;
33027             if(sn.indexOf){
33028                 index = sn.indexOf(node);
33029             }else{
33030                 for(var i = 0, len = sn.length; i < len; i++){
33031                     if(sn[i] == node){
33032                         index = i;
33033                         break;
33034                     }
33035                 }
33036             }
33037             if(index != -1){
33038                 this.selNodes.splice(index, 1);
33039             }
33040             delete this.selMap[node.id];
33041             this.fireEvent("selectionchange", this, this.selNodes);
33042         }
33043     },
33044     
33045     /**
33046      * Clear all selections
33047      */
33048     clearSelections : function(suppressEvent){
33049         var sn = this.selNodes;
33050         if(sn.length > 0){
33051             for(var i = 0, len = sn.length; i < len; i++){
33052                 sn[i].ui.onSelectedChange(false);
33053             }
33054             this.selNodes = [];
33055             this.selMap = {};
33056             if(suppressEvent !== true){
33057                 this.fireEvent("selectionchange", this, this.selNodes);
33058             }
33059         }
33060     },
33061     
33062     /**
33063      * Returns true if the node is selected
33064      * @param {TreeNode} node The node to check
33065      * @return {Boolean}
33066      */
33067     isSelected : function(node){
33068         return this.selMap[node.id] ? true : false;  
33069     },
33070     
33071     /**
33072      * Returns an array of the selected nodes
33073      * @return {Array}
33074      */
33075     getSelectedNodes : function(){
33076         return this.selNodes;    
33077     },
33078
33079     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33080
33081     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33082
33083     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33084 });/*
33085  * Based on:
33086  * Ext JS Library 1.1.1
33087  * Copyright(c) 2006-2007, Ext JS, LLC.
33088  *
33089  * Originally Released Under LGPL - original licence link has changed is not relivant.
33090  *
33091  * Fork - LGPL
33092  * <script type="text/javascript">
33093  */
33094  
33095 /**
33096  * @class Roo.tree.TreeNode
33097  * @extends Roo.data.Node
33098  * @cfg {String} text The text for this node
33099  * @cfg {Boolean} expanded true to start the node expanded
33100  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33101  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33102  * @cfg {Boolean} disabled true to start the node disabled
33103  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33104  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33105  * @cfg {String} cls A css class to be added to the node
33106  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33107  * @cfg {String} href URL of the link used for the node (defaults to #)
33108  * @cfg {String} hrefTarget target frame for the link
33109  * @cfg {String} qtip An Ext QuickTip for the node
33110  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33111  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33112  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33113  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33114  * (defaults to undefined with no checkbox rendered)
33115  * @constructor
33116  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33117  */
33118 Roo.tree.TreeNode = function(attributes){
33119     attributes = attributes || {};
33120     if(typeof attributes == "string"){
33121         attributes = {text: attributes};
33122     }
33123     this.childrenRendered = false;
33124     this.rendered = false;
33125     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33126     this.expanded = attributes.expanded === true;
33127     this.isTarget = attributes.isTarget !== false;
33128     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33129     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33130
33131     /**
33132      * Read-only. The text for this node. To change it use setText().
33133      * @type String
33134      */
33135     this.text = attributes.text;
33136     /**
33137      * True if this node is disabled.
33138      * @type Boolean
33139      */
33140     this.disabled = attributes.disabled === true;
33141
33142     this.addEvents({
33143         /**
33144         * @event textchange
33145         * Fires when the text for this node is changed
33146         * @param {Node} this This node
33147         * @param {String} text The new text
33148         * @param {String} oldText The old text
33149         */
33150         "textchange" : true,
33151         /**
33152         * @event beforeexpand
33153         * Fires before this node is expanded, return false to cancel.
33154         * @param {Node} this This node
33155         * @param {Boolean} deep
33156         * @param {Boolean} anim
33157         */
33158         "beforeexpand" : true,
33159         /**
33160         * @event beforecollapse
33161         * Fires before this node is collapsed, return false to cancel.
33162         * @param {Node} this This node
33163         * @param {Boolean} deep
33164         * @param {Boolean} anim
33165         */
33166         "beforecollapse" : true,
33167         /**
33168         * @event expand
33169         * Fires when this node is expanded
33170         * @param {Node} this This node
33171         */
33172         "expand" : true,
33173         /**
33174         * @event disabledchange
33175         * Fires when the disabled status of this node changes
33176         * @param {Node} this This node
33177         * @param {Boolean} disabled
33178         */
33179         "disabledchange" : true,
33180         /**
33181         * @event collapse
33182         * Fires when this node is collapsed
33183         * @param {Node} this This node
33184         */
33185         "collapse" : true,
33186         /**
33187         * @event beforeclick
33188         * Fires before click processing. Return false to cancel the default action.
33189         * @param {Node} this This node
33190         * @param {Roo.EventObject} e The event object
33191         */
33192         "beforeclick":true,
33193         /**
33194         * @event checkchange
33195         * Fires when a node with a checkbox's checked property changes
33196         * @param {Node} this This node
33197         * @param {Boolean} checked
33198         */
33199         "checkchange":true,
33200         /**
33201         * @event click
33202         * Fires when this node is clicked
33203         * @param {Node} this This node
33204         * @param {Roo.EventObject} e The event object
33205         */
33206         "click":true,
33207         /**
33208         * @event dblclick
33209         * Fires when this node is double clicked
33210         * @param {Node} this This node
33211         * @param {Roo.EventObject} e The event object
33212         */
33213         "dblclick":true,
33214         /**
33215         * @event contextmenu
33216         * Fires when this node is right clicked
33217         * @param {Node} this This node
33218         * @param {Roo.EventObject} e The event object
33219         */
33220         "contextmenu":true,
33221         /**
33222         * @event beforechildrenrendered
33223         * Fires right before the child nodes for this node are rendered
33224         * @param {Node} this This node
33225         */
33226         "beforechildrenrendered":true
33227     });
33228
33229     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33230
33231     /**
33232      * Read-only. The UI for this node
33233      * @type TreeNodeUI
33234      */
33235     this.ui = new uiClass(this);
33236     
33237     // finally support items[]
33238     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33239         return;
33240     }
33241     
33242     
33243     Roo.each(this.attributes.items, function(c) {
33244         this.appendChild(Roo.factory(c,Roo.Tree));
33245     }, this);
33246     delete this.attributes.items;
33247     
33248     
33249     
33250 };
33251 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33252     preventHScroll: true,
33253     /**
33254      * Returns true if this node is expanded
33255      * @return {Boolean}
33256      */
33257     isExpanded : function(){
33258         return this.expanded;
33259     },
33260
33261     /**
33262      * Returns the UI object for this node
33263      * @return {TreeNodeUI}
33264      */
33265     getUI : function(){
33266         return this.ui;
33267     },
33268
33269     // private override
33270     setFirstChild : function(node){
33271         var of = this.firstChild;
33272         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33273         if(this.childrenRendered && of && node != of){
33274             of.renderIndent(true, true);
33275         }
33276         if(this.rendered){
33277             this.renderIndent(true, true);
33278         }
33279     },
33280
33281     // private override
33282     setLastChild : function(node){
33283         var ol = this.lastChild;
33284         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33285         if(this.childrenRendered && ol && node != ol){
33286             ol.renderIndent(true, true);
33287         }
33288         if(this.rendered){
33289             this.renderIndent(true, true);
33290         }
33291     },
33292
33293     // these methods are overridden to provide lazy rendering support
33294     // private override
33295     appendChild : function()
33296     {
33297         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33298         if(node && this.childrenRendered){
33299             node.render();
33300         }
33301         this.ui.updateExpandIcon();
33302         return node;
33303     },
33304
33305     // private override
33306     removeChild : function(node){
33307         this.ownerTree.getSelectionModel().unselect(node);
33308         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33309         // if it's been rendered remove dom node
33310         if(this.childrenRendered){
33311             node.ui.remove();
33312         }
33313         if(this.childNodes.length < 1){
33314             this.collapse(false, false);
33315         }else{
33316             this.ui.updateExpandIcon();
33317         }
33318         if(!this.firstChild) {
33319             this.childrenRendered = false;
33320         }
33321         return node;
33322     },
33323
33324     // private override
33325     insertBefore : function(node, refNode){
33326         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33327         if(newNode && refNode && this.childrenRendered){
33328             node.render();
33329         }
33330         this.ui.updateExpandIcon();
33331         return newNode;
33332     },
33333
33334     /**
33335      * Sets the text for this node
33336      * @param {String} text
33337      */
33338     setText : function(text){
33339         var oldText = this.text;
33340         this.text = text;
33341         this.attributes.text = text;
33342         if(this.rendered){ // event without subscribing
33343             this.ui.onTextChange(this, text, oldText);
33344         }
33345         this.fireEvent("textchange", this, text, oldText);
33346     },
33347
33348     /**
33349      * Triggers selection of this node
33350      */
33351     select : function(){
33352         this.getOwnerTree().getSelectionModel().select(this);
33353     },
33354
33355     /**
33356      * Triggers deselection of this node
33357      */
33358     unselect : function(){
33359         this.getOwnerTree().getSelectionModel().unselect(this);
33360     },
33361
33362     /**
33363      * Returns true if this node is selected
33364      * @return {Boolean}
33365      */
33366     isSelected : function(){
33367         return this.getOwnerTree().getSelectionModel().isSelected(this);
33368     },
33369
33370     /**
33371      * Expand this node.
33372      * @param {Boolean} deep (optional) True to expand all children as well
33373      * @param {Boolean} anim (optional) false to cancel the default animation
33374      * @param {Function} callback (optional) A callback to be called when
33375      * expanding this node completes (does not wait for deep expand to complete).
33376      * Called with 1 parameter, this node.
33377      */
33378     expand : function(deep, anim, callback){
33379         if(!this.expanded){
33380             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33381                 return;
33382             }
33383             if(!this.childrenRendered){
33384                 this.renderChildren();
33385             }
33386             this.expanded = true;
33387             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33388                 this.ui.animExpand(function(){
33389                     this.fireEvent("expand", this);
33390                     if(typeof callback == "function"){
33391                         callback(this);
33392                     }
33393                     if(deep === true){
33394                         this.expandChildNodes(true);
33395                     }
33396                 }.createDelegate(this));
33397                 return;
33398             }else{
33399                 this.ui.expand();
33400                 this.fireEvent("expand", this);
33401                 if(typeof callback == "function"){
33402                     callback(this);
33403                 }
33404             }
33405         }else{
33406            if(typeof callback == "function"){
33407                callback(this);
33408            }
33409         }
33410         if(deep === true){
33411             this.expandChildNodes(true);
33412         }
33413     },
33414
33415     isHiddenRoot : function(){
33416         return this.isRoot && !this.getOwnerTree().rootVisible;
33417     },
33418
33419     /**
33420      * Collapse this node.
33421      * @param {Boolean} deep (optional) True to collapse all children as well
33422      * @param {Boolean} anim (optional) false to cancel the default animation
33423      */
33424     collapse : function(deep, anim){
33425         if(this.expanded && !this.isHiddenRoot()){
33426             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33427                 return;
33428             }
33429             this.expanded = false;
33430             if((this.getOwnerTree().animate && anim !== false) || anim){
33431                 this.ui.animCollapse(function(){
33432                     this.fireEvent("collapse", this);
33433                     if(deep === true){
33434                         this.collapseChildNodes(true);
33435                     }
33436                 }.createDelegate(this));
33437                 return;
33438             }else{
33439                 this.ui.collapse();
33440                 this.fireEvent("collapse", this);
33441             }
33442         }
33443         if(deep === true){
33444             var cs = this.childNodes;
33445             for(var i = 0, len = cs.length; i < len; i++) {
33446                 cs[i].collapse(true, false);
33447             }
33448         }
33449     },
33450
33451     // private
33452     delayedExpand : function(delay){
33453         if(!this.expandProcId){
33454             this.expandProcId = this.expand.defer(delay, this);
33455         }
33456     },
33457
33458     // private
33459     cancelExpand : function(){
33460         if(this.expandProcId){
33461             clearTimeout(this.expandProcId);
33462         }
33463         this.expandProcId = false;
33464     },
33465
33466     /**
33467      * Toggles expanded/collapsed state of the node
33468      */
33469     toggle : function(){
33470         if(this.expanded){
33471             this.collapse();
33472         }else{
33473             this.expand();
33474         }
33475     },
33476
33477     /**
33478      * Ensures all parent nodes are expanded
33479      */
33480     ensureVisible : function(callback){
33481         var tree = this.getOwnerTree();
33482         tree.expandPath(this.parentNode.getPath(), false, function(){
33483             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33484             Roo.callback(callback);
33485         }.createDelegate(this));
33486     },
33487
33488     /**
33489      * Expand all child nodes
33490      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33491      */
33492     expandChildNodes : function(deep){
33493         var cs = this.childNodes;
33494         for(var i = 0, len = cs.length; i < len; i++) {
33495                 cs[i].expand(deep);
33496         }
33497     },
33498
33499     /**
33500      * Collapse all child nodes
33501      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33502      */
33503     collapseChildNodes : function(deep){
33504         var cs = this.childNodes;
33505         for(var i = 0, len = cs.length; i < len; i++) {
33506                 cs[i].collapse(deep);
33507         }
33508     },
33509
33510     /**
33511      * Disables this node
33512      */
33513     disable : function(){
33514         this.disabled = true;
33515         this.unselect();
33516         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33517             this.ui.onDisableChange(this, true);
33518         }
33519         this.fireEvent("disabledchange", this, true);
33520     },
33521
33522     /**
33523      * Enables this node
33524      */
33525     enable : function(){
33526         this.disabled = false;
33527         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33528             this.ui.onDisableChange(this, false);
33529         }
33530         this.fireEvent("disabledchange", this, false);
33531     },
33532
33533     // private
33534     renderChildren : function(suppressEvent){
33535         if(suppressEvent !== false){
33536             this.fireEvent("beforechildrenrendered", this);
33537         }
33538         var cs = this.childNodes;
33539         for(var i = 0, len = cs.length; i < len; i++){
33540             cs[i].render(true);
33541         }
33542         this.childrenRendered = true;
33543     },
33544
33545     // private
33546     sort : function(fn, scope){
33547         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33548         if(this.childrenRendered){
33549             var cs = this.childNodes;
33550             for(var i = 0, len = cs.length; i < len; i++){
33551                 cs[i].render(true);
33552             }
33553         }
33554     },
33555
33556     // private
33557     render : function(bulkRender){
33558         this.ui.render(bulkRender);
33559         if(!this.rendered){
33560             this.rendered = true;
33561             if(this.expanded){
33562                 this.expanded = false;
33563                 this.expand(false, false);
33564             }
33565         }
33566     },
33567
33568     // private
33569     renderIndent : function(deep, refresh){
33570         if(refresh){
33571             this.ui.childIndent = null;
33572         }
33573         this.ui.renderIndent();
33574         if(deep === true && this.childrenRendered){
33575             var cs = this.childNodes;
33576             for(var i = 0, len = cs.length; i < len; i++){
33577                 cs[i].renderIndent(true, refresh);
33578             }
33579         }
33580     }
33581 });/*
33582  * Based on:
33583  * Ext JS Library 1.1.1
33584  * Copyright(c) 2006-2007, Ext JS, LLC.
33585  *
33586  * Originally Released Under LGPL - original licence link has changed is not relivant.
33587  *
33588  * Fork - LGPL
33589  * <script type="text/javascript">
33590  */
33591  
33592 /**
33593  * @class Roo.tree.AsyncTreeNode
33594  * @extends Roo.tree.TreeNode
33595  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33596  * @constructor
33597  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33598  */
33599  Roo.tree.AsyncTreeNode = function(config){
33600     this.loaded = false;
33601     this.loading = false;
33602     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33603     /**
33604     * @event beforeload
33605     * Fires before this node is loaded, return false to cancel
33606     * @param {Node} this This node
33607     */
33608     this.addEvents({'beforeload':true, 'load': true});
33609     /**
33610     * @event load
33611     * Fires when this node is loaded
33612     * @param {Node} this This node
33613     */
33614     /**
33615      * The loader used by this node (defaults to using the tree's defined loader)
33616      * @type TreeLoader
33617      * @property loader
33618      */
33619 };
33620 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33621     expand : function(deep, anim, callback){
33622         if(this.loading){ // if an async load is already running, waiting til it's done
33623             var timer;
33624             var f = function(){
33625                 if(!this.loading){ // done loading
33626                     clearInterval(timer);
33627                     this.expand(deep, anim, callback);
33628                 }
33629             }.createDelegate(this);
33630             timer = setInterval(f, 200);
33631             return;
33632         }
33633         if(!this.loaded){
33634             if(this.fireEvent("beforeload", this) === false){
33635                 return;
33636             }
33637             this.loading = true;
33638             this.ui.beforeLoad(this);
33639             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33640             if(loader){
33641                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33642                 return;
33643             }
33644         }
33645         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33646     },
33647     
33648     /**
33649      * Returns true if this node is currently loading
33650      * @return {Boolean}
33651      */
33652     isLoading : function(){
33653         return this.loading;  
33654     },
33655     
33656     loadComplete : function(deep, anim, callback){
33657         this.loading = false;
33658         this.loaded = true;
33659         this.ui.afterLoad(this);
33660         this.fireEvent("load", this);
33661         this.expand(deep, anim, callback);
33662     },
33663     
33664     /**
33665      * Returns true if this node has been loaded
33666      * @return {Boolean}
33667      */
33668     isLoaded : function(){
33669         return this.loaded;
33670     },
33671     
33672     hasChildNodes : function(){
33673         if(!this.isLeaf() && !this.loaded){
33674             return true;
33675         }else{
33676             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33677         }
33678     },
33679
33680     /**
33681      * Trigger a reload for this node
33682      * @param {Function} callback
33683      */
33684     reload : function(callback){
33685         this.collapse(false, false);
33686         while(this.firstChild){
33687             this.removeChild(this.firstChild);
33688         }
33689         this.childrenRendered = false;
33690         this.loaded = false;
33691         if(this.isHiddenRoot()){
33692             this.expanded = false;
33693         }
33694         this.expand(false, false, callback);
33695     }
33696 });/*
33697  * Based on:
33698  * Ext JS Library 1.1.1
33699  * Copyright(c) 2006-2007, Ext JS, LLC.
33700  *
33701  * Originally Released Under LGPL - original licence link has changed is not relivant.
33702  *
33703  * Fork - LGPL
33704  * <script type="text/javascript">
33705  */
33706  
33707 /**
33708  * @class Roo.tree.TreeNodeUI
33709  * @constructor
33710  * @param {Object} node The node to render
33711  * The TreeNode UI implementation is separate from the
33712  * tree implementation. Unless you are customizing the tree UI,
33713  * you should never have to use this directly.
33714  */
33715 Roo.tree.TreeNodeUI = function(node){
33716     this.node = node;
33717     this.rendered = false;
33718     this.animating = false;
33719     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33720 };
33721
33722 Roo.tree.TreeNodeUI.prototype = {
33723     removeChild : function(node){
33724         if(this.rendered){
33725             this.ctNode.removeChild(node.ui.getEl());
33726         }
33727     },
33728
33729     beforeLoad : function(){
33730          this.addClass("x-tree-node-loading");
33731     },
33732
33733     afterLoad : function(){
33734          this.removeClass("x-tree-node-loading");
33735     },
33736
33737     onTextChange : function(node, text, oldText){
33738         if(this.rendered){
33739             this.textNode.innerHTML = text;
33740         }
33741     },
33742
33743     onDisableChange : function(node, state){
33744         this.disabled = state;
33745         if(state){
33746             this.addClass("x-tree-node-disabled");
33747         }else{
33748             this.removeClass("x-tree-node-disabled");
33749         }
33750     },
33751
33752     onSelectedChange : function(state){
33753         if(state){
33754             this.focus();
33755             this.addClass("x-tree-selected");
33756         }else{
33757             //this.blur();
33758             this.removeClass("x-tree-selected");
33759         }
33760     },
33761
33762     onMove : function(tree, node, oldParent, newParent, index, refNode){
33763         this.childIndent = null;
33764         if(this.rendered){
33765             var targetNode = newParent.ui.getContainer();
33766             if(!targetNode){//target not rendered
33767                 this.holder = document.createElement("div");
33768                 this.holder.appendChild(this.wrap);
33769                 return;
33770             }
33771             var insertBefore = refNode ? refNode.ui.getEl() : null;
33772             if(insertBefore){
33773                 targetNode.insertBefore(this.wrap, insertBefore);
33774             }else{
33775                 targetNode.appendChild(this.wrap);
33776             }
33777             this.node.renderIndent(true);
33778         }
33779     },
33780
33781     addClass : function(cls){
33782         if(this.elNode){
33783             Roo.fly(this.elNode).addClass(cls);
33784         }
33785     },
33786
33787     removeClass : function(cls){
33788         if(this.elNode){
33789             Roo.fly(this.elNode).removeClass(cls);
33790         }
33791     },
33792
33793     remove : function(){
33794         if(this.rendered){
33795             this.holder = document.createElement("div");
33796             this.holder.appendChild(this.wrap);
33797         }
33798     },
33799
33800     fireEvent : function(){
33801         return this.node.fireEvent.apply(this.node, arguments);
33802     },
33803
33804     initEvents : function(){
33805         this.node.on("move", this.onMove, this);
33806         var E = Roo.EventManager;
33807         var a = this.anchor;
33808
33809         var el = Roo.fly(a, '_treeui');
33810
33811         if(Roo.isOpera){ // opera render bug ignores the CSS
33812             el.setStyle("text-decoration", "none");
33813         }
33814
33815         el.on("click", this.onClick, this);
33816         el.on("dblclick", this.onDblClick, this);
33817
33818         if(this.checkbox){
33819             Roo.EventManager.on(this.checkbox,
33820                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33821         }
33822
33823         el.on("contextmenu", this.onContextMenu, this);
33824
33825         var icon = Roo.fly(this.iconNode);
33826         icon.on("click", this.onClick, this);
33827         icon.on("dblclick", this.onDblClick, this);
33828         icon.on("contextmenu", this.onContextMenu, this);
33829         E.on(this.ecNode, "click", this.ecClick, this, true);
33830
33831         if(this.node.disabled){
33832             this.addClass("x-tree-node-disabled");
33833         }
33834         if(this.node.hidden){
33835             this.addClass("x-tree-node-disabled");
33836         }
33837         var ot = this.node.getOwnerTree();
33838         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33839         if(dd && (!this.node.isRoot || ot.rootVisible)){
33840             Roo.dd.Registry.register(this.elNode, {
33841                 node: this.node,
33842                 handles: this.getDDHandles(),
33843                 isHandle: false
33844             });
33845         }
33846     },
33847
33848     getDDHandles : function(){
33849         return [this.iconNode, this.textNode];
33850     },
33851
33852     hide : function(){
33853         if(this.rendered){
33854             this.wrap.style.display = "none";
33855         }
33856     },
33857
33858     show : function(){
33859         if(this.rendered){
33860             this.wrap.style.display = "";
33861         }
33862     },
33863
33864     onContextMenu : function(e){
33865         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33866             e.preventDefault();
33867             this.focus();
33868             this.fireEvent("contextmenu", this.node, e);
33869         }
33870     },
33871
33872     onClick : function(e){
33873         if(this.dropping){
33874             e.stopEvent();
33875             return;
33876         }
33877         if(this.fireEvent("beforeclick", this.node, e) !== false){
33878             if(!this.disabled && this.node.attributes.href){
33879                 this.fireEvent("click", this.node, e);
33880                 return;
33881             }
33882             e.preventDefault();
33883             if(this.disabled){
33884                 return;
33885             }
33886
33887             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33888                 this.node.toggle();
33889             }
33890
33891             this.fireEvent("click", this.node, e);
33892         }else{
33893             e.stopEvent();
33894         }
33895     },
33896
33897     onDblClick : function(e){
33898         e.preventDefault();
33899         if(this.disabled){
33900             return;
33901         }
33902         if(this.checkbox){
33903             this.toggleCheck();
33904         }
33905         if(!this.animating && this.node.hasChildNodes()){
33906             this.node.toggle();
33907         }
33908         this.fireEvent("dblclick", this.node, e);
33909     },
33910
33911     onCheckChange : function(){
33912         var checked = this.checkbox.checked;
33913         this.node.attributes.checked = checked;
33914         this.fireEvent('checkchange', this.node, checked);
33915     },
33916
33917     ecClick : function(e){
33918         if(!this.animating && this.node.hasChildNodes()){
33919             this.node.toggle();
33920         }
33921     },
33922
33923     startDrop : function(){
33924         this.dropping = true;
33925     },
33926
33927     // delayed drop so the click event doesn't get fired on a drop
33928     endDrop : function(){
33929        setTimeout(function(){
33930            this.dropping = false;
33931        }.createDelegate(this), 50);
33932     },
33933
33934     expand : function(){
33935         this.updateExpandIcon();
33936         this.ctNode.style.display = "";
33937     },
33938
33939     focus : function(){
33940         if(!this.node.preventHScroll){
33941             try{this.anchor.focus();
33942             }catch(e){}
33943         }else if(!Roo.isIE){
33944             try{
33945                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33946                 var l = noscroll.scrollLeft;
33947                 this.anchor.focus();
33948                 noscroll.scrollLeft = l;
33949             }catch(e){}
33950         }
33951     },
33952
33953     toggleCheck : function(value){
33954         var cb = this.checkbox;
33955         if(cb){
33956             cb.checked = (value === undefined ? !cb.checked : value);
33957         }
33958     },
33959
33960     blur : function(){
33961         try{
33962             this.anchor.blur();
33963         }catch(e){}
33964     },
33965
33966     animExpand : function(callback){
33967         var ct = Roo.get(this.ctNode);
33968         ct.stopFx();
33969         if(!this.node.hasChildNodes()){
33970             this.updateExpandIcon();
33971             this.ctNode.style.display = "";
33972             Roo.callback(callback);
33973             return;
33974         }
33975         this.animating = true;
33976         this.updateExpandIcon();
33977
33978         ct.slideIn('t', {
33979            callback : function(){
33980                this.animating = false;
33981                Roo.callback(callback);
33982             },
33983             scope: this,
33984             duration: this.node.ownerTree.duration || .25
33985         });
33986     },
33987
33988     highlight : function(){
33989         var tree = this.node.getOwnerTree();
33990         Roo.fly(this.wrap).highlight(
33991             tree.hlColor || "C3DAF9",
33992             {endColor: tree.hlBaseColor}
33993         );
33994     },
33995
33996     collapse : function(){
33997         this.updateExpandIcon();
33998         this.ctNode.style.display = "none";
33999     },
34000
34001     animCollapse : function(callback){
34002         var ct = Roo.get(this.ctNode);
34003         ct.enableDisplayMode('block');
34004         ct.stopFx();
34005
34006         this.animating = true;
34007         this.updateExpandIcon();
34008
34009         ct.slideOut('t', {
34010             callback : function(){
34011                this.animating = false;
34012                Roo.callback(callback);
34013             },
34014             scope: this,
34015             duration: this.node.ownerTree.duration || .25
34016         });
34017     },
34018
34019     getContainer : function(){
34020         return this.ctNode;
34021     },
34022
34023     getEl : function(){
34024         return this.wrap;
34025     },
34026
34027     appendDDGhost : function(ghostNode){
34028         ghostNode.appendChild(this.elNode.cloneNode(true));
34029     },
34030
34031     getDDRepairXY : function(){
34032         return Roo.lib.Dom.getXY(this.iconNode);
34033     },
34034
34035     onRender : function(){
34036         this.render();
34037     },
34038
34039     render : function(bulkRender){
34040         var n = this.node, a = n.attributes;
34041         var targetNode = n.parentNode ?
34042               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34043
34044         if(!this.rendered){
34045             this.rendered = true;
34046
34047             this.renderElements(n, a, targetNode, bulkRender);
34048
34049             if(a.qtip){
34050                if(this.textNode.setAttributeNS){
34051                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34052                    if(a.qtipTitle){
34053                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34054                    }
34055                }else{
34056                    this.textNode.setAttribute("ext:qtip", a.qtip);
34057                    if(a.qtipTitle){
34058                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34059                    }
34060                }
34061             }else if(a.qtipCfg){
34062                 a.qtipCfg.target = Roo.id(this.textNode);
34063                 Roo.QuickTips.register(a.qtipCfg);
34064             }
34065             this.initEvents();
34066             if(!this.node.expanded){
34067                 this.updateExpandIcon();
34068             }
34069         }else{
34070             if(bulkRender === true) {
34071                 targetNode.appendChild(this.wrap);
34072             }
34073         }
34074     },
34075
34076     renderElements : function(n, a, targetNode, bulkRender)
34077     {
34078         // add some indent caching, this helps performance when rendering a large tree
34079         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34080         var t = n.getOwnerTree();
34081         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34082         if (typeof(n.attributes.html) != 'undefined') {
34083             txt = n.attributes.html;
34084         }
34085         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34086         var cb = typeof a.checked == 'boolean';
34087         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34088         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34089             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34090             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34091             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34092             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34093             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34094              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34095                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34096             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34097             "</li>"];
34098
34099         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34100             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34101                                 n.nextSibling.ui.getEl(), buf.join(""));
34102         }else{
34103             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34104         }
34105
34106         this.elNode = this.wrap.childNodes[0];
34107         this.ctNode = this.wrap.childNodes[1];
34108         var cs = this.elNode.childNodes;
34109         this.indentNode = cs[0];
34110         this.ecNode = cs[1];
34111         this.iconNode = cs[2];
34112         var index = 3;
34113         if(cb){
34114             this.checkbox = cs[3];
34115             index++;
34116         }
34117         this.anchor = cs[index];
34118         this.textNode = cs[index].firstChild;
34119     },
34120
34121     getAnchor : function(){
34122         return this.anchor;
34123     },
34124
34125     getTextEl : function(){
34126         return this.textNode;
34127     },
34128
34129     getIconEl : function(){
34130         return this.iconNode;
34131     },
34132
34133     isChecked : function(){
34134         return this.checkbox ? this.checkbox.checked : false;
34135     },
34136
34137     updateExpandIcon : function(){
34138         if(this.rendered){
34139             var n = this.node, c1, c2;
34140             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34141             var hasChild = n.hasChildNodes();
34142             if(hasChild){
34143                 if(n.expanded){
34144                     cls += "-minus";
34145                     c1 = "x-tree-node-collapsed";
34146                     c2 = "x-tree-node-expanded";
34147                 }else{
34148                     cls += "-plus";
34149                     c1 = "x-tree-node-expanded";
34150                     c2 = "x-tree-node-collapsed";
34151                 }
34152                 if(this.wasLeaf){
34153                     this.removeClass("x-tree-node-leaf");
34154                     this.wasLeaf = false;
34155                 }
34156                 if(this.c1 != c1 || this.c2 != c2){
34157                     Roo.fly(this.elNode).replaceClass(c1, c2);
34158                     this.c1 = c1; this.c2 = c2;
34159                 }
34160             }else{
34161                 // this changes non-leafs into leafs if they have no children.
34162                 // it's not very rational behaviour..
34163                 
34164                 if(!this.wasLeaf && this.node.leaf){
34165                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34166                     delete this.c1;
34167                     delete this.c2;
34168                     this.wasLeaf = true;
34169                 }
34170             }
34171             var ecc = "x-tree-ec-icon "+cls;
34172             if(this.ecc != ecc){
34173                 this.ecNode.className = ecc;
34174                 this.ecc = ecc;
34175             }
34176         }
34177     },
34178
34179     getChildIndent : function(){
34180         if(!this.childIndent){
34181             var buf = [];
34182             var p = this.node;
34183             while(p){
34184                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34185                     if(!p.isLast()) {
34186                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34187                     } else {
34188                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34189                     }
34190                 }
34191                 p = p.parentNode;
34192             }
34193             this.childIndent = buf.join("");
34194         }
34195         return this.childIndent;
34196     },
34197
34198     renderIndent : function(){
34199         if(this.rendered){
34200             var indent = "";
34201             var p = this.node.parentNode;
34202             if(p){
34203                 indent = p.ui.getChildIndent();
34204             }
34205             if(this.indentMarkup != indent){ // don't rerender if not required
34206                 this.indentNode.innerHTML = indent;
34207                 this.indentMarkup = indent;
34208             }
34209             this.updateExpandIcon();
34210         }
34211     }
34212 };
34213
34214 Roo.tree.RootTreeNodeUI = function(){
34215     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34216 };
34217 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34218     render : function(){
34219         if(!this.rendered){
34220             var targetNode = this.node.ownerTree.innerCt.dom;
34221             this.node.expanded = true;
34222             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34223             this.wrap = this.ctNode = targetNode.firstChild;
34224         }
34225     },
34226     collapse : function(){
34227     },
34228     expand : function(){
34229     }
34230 });/*
34231  * Based on:
34232  * Ext JS Library 1.1.1
34233  * Copyright(c) 2006-2007, Ext JS, LLC.
34234  *
34235  * Originally Released Under LGPL - original licence link has changed is not relivant.
34236  *
34237  * Fork - LGPL
34238  * <script type="text/javascript">
34239  */
34240 /**
34241  * @class Roo.tree.TreeLoader
34242  * @extends Roo.util.Observable
34243  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34244  * nodes from a specified URL. The response must be a javascript Array definition
34245  * who's elements are node definition objects. eg:
34246  * <pre><code>
34247 {  success : true,
34248    data :      [
34249    
34250     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34251     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34252     ]
34253 }
34254
34255
34256 </code></pre>
34257  * <br><br>
34258  * The old style respose with just an array is still supported, but not recommended.
34259  * <br><br>
34260  *
34261  * A server request is sent, and child nodes are loaded only when a node is expanded.
34262  * The loading node's id is passed to the server under the parameter name "node" to
34263  * enable the server to produce the correct child nodes.
34264  * <br><br>
34265  * To pass extra parameters, an event handler may be attached to the "beforeload"
34266  * event, and the parameters specified in the TreeLoader's baseParams property:
34267  * <pre><code>
34268     myTreeLoader.on("beforeload", function(treeLoader, node) {
34269         this.baseParams.category = node.attributes.category;
34270     }, this);
34271 </code></pre><
34272  * This would pass an HTTP parameter called "category" to the server containing
34273  * the value of the Node's "category" attribute.
34274  * @constructor
34275  * Creates a new Treeloader.
34276  * @param {Object} config A config object containing config properties.
34277  */
34278 Roo.tree.TreeLoader = function(config){
34279     this.baseParams = {};
34280     this.requestMethod = "POST";
34281     Roo.apply(this, config);
34282
34283     this.addEvents({
34284     
34285         /**
34286          * @event beforeload
34287          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34288          * @param {Object} This TreeLoader object.
34289          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34290          * @param {Object} callback The callback function specified in the {@link #load} call.
34291          */
34292         beforeload : true,
34293         /**
34294          * @event load
34295          * Fires when the node has been successfuly loaded.
34296          * @param {Object} This TreeLoader object.
34297          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34298          * @param {Object} response The response object containing the data from the server.
34299          */
34300         load : true,
34301         /**
34302          * @event loadexception
34303          * Fires if the network request failed.
34304          * @param {Object} This TreeLoader object.
34305          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34306          * @param {Object} response The response object containing the data from the server.
34307          */
34308         loadexception : true,
34309         /**
34310          * @event create
34311          * Fires before a node is created, enabling you to return custom Node types 
34312          * @param {Object} This TreeLoader object.
34313          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34314          */
34315         create : true
34316     });
34317
34318     Roo.tree.TreeLoader.superclass.constructor.call(this);
34319 };
34320
34321 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34322     /**
34323     * @cfg {String} dataUrl The URL from which to request a Json string which
34324     * specifies an array of node definition object representing the child nodes
34325     * to be loaded.
34326     */
34327     /**
34328     * @cfg {String} requestMethod either GET or POST
34329     * defaults to POST (due to BC)
34330     * to be loaded.
34331     */
34332     /**
34333     * @cfg {Object} baseParams (optional) An object containing properties which
34334     * specify HTTP parameters to be passed to each request for child nodes.
34335     */
34336     /**
34337     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34338     * created by this loader. If the attributes sent by the server have an attribute in this object,
34339     * they take priority.
34340     */
34341     /**
34342     * @cfg {Object} uiProviders (optional) An object containing properties which
34343     * 
34344     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34345     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34346     * <i>uiProvider</i> attribute of a returned child node is a string rather
34347     * than a reference to a TreeNodeUI implementation, this that string value
34348     * is used as a property name in the uiProviders object. You can define the provider named
34349     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34350     */
34351     uiProviders : {},
34352
34353     /**
34354     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34355     * child nodes before loading.
34356     */
34357     clearOnLoad : true,
34358
34359     /**
34360     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34361     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34362     * Grid query { data : [ .....] }
34363     */
34364     
34365     root : false,
34366      /**
34367     * @cfg {String} queryParam (optional) 
34368     * Name of the query as it will be passed on the querystring (defaults to 'node')
34369     * eg. the request will be ?node=[id]
34370     */
34371     
34372     
34373     queryParam: false,
34374     
34375     /**
34376      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34377      * This is called automatically when a node is expanded, but may be used to reload
34378      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34379      * @param {Roo.tree.TreeNode} node
34380      * @param {Function} callback
34381      */
34382     load : function(node, callback){
34383         if(this.clearOnLoad){
34384             while(node.firstChild){
34385                 node.removeChild(node.firstChild);
34386             }
34387         }
34388         if(node.attributes.children){ // preloaded json children
34389             var cs = node.attributes.children;
34390             for(var i = 0, len = cs.length; i < len; i++){
34391                 node.appendChild(this.createNode(cs[i]));
34392             }
34393             if(typeof callback == "function"){
34394                 callback();
34395             }
34396         }else if(this.dataUrl){
34397             this.requestData(node, callback);
34398         }
34399     },
34400
34401     getParams: function(node){
34402         var buf = [], bp = this.baseParams;
34403         for(var key in bp){
34404             if(typeof bp[key] != "function"){
34405                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34406             }
34407         }
34408         var n = this.queryParam === false ? 'node' : this.queryParam;
34409         buf.push(n + "=", encodeURIComponent(node.id));
34410         return buf.join("");
34411     },
34412
34413     requestData : function(node, callback){
34414         if(this.fireEvent("beforeload", this, node, callback) !== false){
34415             this.transId = Roo.Ajax.request({
34416                 method:this.requestMethod,
34417                 url: this.dataUrl||this.url,
34418                 success: this.handleResponse,
34419                 failure: this.handleFailure,
34420                 scope: this,
34421                 argument: {callback: callback, node: node},
34422                 params: this.getParams(node)
34423             });
34424         }else{
34425             // if the load is cancelled, make sure we notify
34426             // the node that we are done
34427             if(typeof callback == "function"){
34428                 callback();
34429             }
34430         }
34431     },
34432
34433     isLoading : function(){
34434         return this.transId ? true : false;
34435     },
34436
34437     abort : function(){
34438         if(this.isLoading()){
34439             Roo.Ajax.abort(this.transId);
34440         }
34441     },
34442
34443     // private
34444     createNode : function(attr)
34445     {
34446         // apply baseAttrs, nice idea Corey!
34447         if(this.baseAttrs){
34448             Roo.applyIf(attr, this.baseAttrs);
34449         }
34450         if(this.applyLoader !== false){
34451             attr.loader = this;
34452         }
34453         // uiProvider = depreciated..
34454         
34455         if(typeof(attr.uiProvider) == 'string'){
34456            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34457                 /**  eval:var:attr */ eval(attr.uiProvider);
34458         }
34459         if(typeof(this.uiProviders['default']) != 'undefined') {
34460             attr.uiProvider = this.uiProviders['default'];
34461         }
34462         
34463         this.fireEvent('create', this, attr);
34464         
34465         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34466         return(attr.leaf ?
34467                         new Roo.tree.TreeNode(attr) :
34468                         new Roo.tree.AsyncTreeNode(attr));
34469     },
34470
34471     processResponse : function(response, node, callback)
34472     {
34473         var json = response.responseText;
34474         try {
34475             
34476             var o = Roo.decode(json);
34477             
34478             if (this.root === false && typeof(o.success) != undefined) {
34479                 this.root = 'data'; // the default behaviour for list like data..
34480                 }
34481                 
34482             if (this.root !== false &&  !o.success) {
34483                 // it's a failure condition.
34484                 var a = response.argument;
34485                 this.fireEvent("loadexception", this, a.node, response);
34486                 Roo.log("Load failed - should have a handler really");
34487                 return;
34488             }
34489             
34490             
34491             
34492             if (this.root !== false) {
34493                  o = o[this.root];
34494             }
34495             
34496             for(var i = 0, len = o.length; i < len; i++){
34497                 var n = this.createNode(o[i]);
34498                 if(n){
34499                     node.appendChild(n);
34500                 }
34501             }
34502             if(typeof callback == "function"){
34503                 callback(this, node);
34504             }
34505         }catch(e){
34506             this.handleFailure(response);
34507         }
34508     },
34509
34510     handleResponse : function(response){
34511         this.transId = false;
34512         var a = response.argument;
34513         this.processResponse(response, a.node, a.callback);
34514         this.fireEvent("load", this, a.node, response);
34515     },
34516
34517     handleFailure : function(response)
34518     {
34519         // should handle failure better..
34520         this.transId = false;
34521         var a = response.argument;
34522         this.fireEvent("loadexception", this, a.node, response);
34523         if(typeof a.callback == "function"){
34524             a.callback(this, a.node);
34525         }
34526     }
34527 });/*
34528  * Based on:
34529  * Ext JS Library 1.1.1
34530  * Copyright(c) 2006-2007, Ext JS, LLC.
34531  *
34532  * Originally Released Under LGPL - original licence link has changed is not relivant.
34533  *
34534  * Fork - LGPL
34535  * <script type="text/javascript">
34536  */
34537
34538 /**
34539 * @class Roo.tree.TreeFilter
34540 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34541 * @param {TreePanel} tree
34542 * @param {Object} config (optional)
34543  */
34544 Roo.tree.TreeFilter = function(tree, config){
34545     this.tree = tree;
34546     this.filtered = {};
34547     Roo.apply(this, config);
34548 };
34549
34550 Roo.tree.TreeFilter.prototype = {
34551     clearBlank:false,
34552     reverse:false,
34553     autoClear:false,
34554     remove:false,
34555
34556      /**
34557      * Filter the data by a specific attribute.
34558      * @param {String/RegExp} value Either string that the attribute value
34559      * should start with or a RegExp to test against the attribute
34560      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34561      * @param {TreeNode} startNode (optional) The node to start the filter at.
34562      */
34563     filter : function(value, attr, startNode){
34564         attr = attr || "text";
34565         var f;
34566         if(typeof value == "string"){
34567             var vlen = value.length;
34568             // auto clear empty filter
34569             if(vlen == 0 && this.clearBlank){
34570                 this.clear();
34571                 return;
34572             }
34573             value = value.toLowerCase();
34574             f = function(n){
34575                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34576             };
34577         }else if(value.exec){ // regex?
34578             f = function(n){
34579                 return value.test(n.attributes[attr]);
34580             };
34581         }else{
34582             throw 'Illegal filter type, must be string or regex';
34583         }
34584         this.filterBy(f, null, startNode);
34585         },
34586
34587     /**
34588      * Filter by a function. The passed function will be called with each
34589      * node in the tree (or from the startNode). If the function returns true, the node is kept
34590      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34591      * @param {Function} fn The filter function
34592      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34593      */
34594     filterBy : function(fn, scope, startNode){
34595         startNode = startNode || this.tree.root;
34596         if(this.autoClear){
34597             this.clear();
34598         }
34599         var af = this.filtered, rv = this.reverse;
34600         var f = function(n){
34601             if(n == startNode){
34602                 return true;
34603             }
34604             if(af[n.id]){
34605                 return false;
34606             }
34607             var m = fn.call(scope || n, n);
34608             if(!m || rv){
34609                 af[n.id] = n;
34610                 n.ui.hide();
34611                 return false;
34612             }
34613             return true;
34614         };
34615         startNode.cascade(f);
34616         if(this.remove){
34617            for(var id in af){
34618                if(typeof id != "function"){
34619                    var n = af[id];
34620                    if(n && n.parentNode){
34621                        n.parentNode.removeChild(n);
34622                    }
34623                }
34624            }
34625         }
34626     },
34627
34628     /**
34629      * Clears the current filter. Note: with the "remove" option
34630      * set a filter cannot be cleared.
34631      */
34632     clear : function(){
34633         var t = this.tree;
34634         var af = this.filtered;
34635         for(var id in af){
34636             if(typeof id != "function"){
34637                 var n = af[id];
34638                 if(n){
34639                     n.ui.show();
34640                 }
34641             }
34642         }
34643         this.filtered = {};
34644     }
34645 };
34646 /*
34647  * Based on:
34648  * Ext JS Library 1.1.1
34649  * Copyright(c) 2006-2007, Ext JS, LLC.
34650  *
34651  * Originally Released Under LGPL - original licence link has changed is not relivant.
34652  *
34653  * Fork - LGPL
34654  * <script type="text/javascript">
34655  */
34656  
34657
34658 /**
34659  * @class Roo.tree.TreeSorter
34660  * Provides sorting of nodes in a TreePanel
34661  * 
34662  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34663  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34664  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34665  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34666  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34667  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34668  * @constructor
34669  * @param {TreePanel} tree
34670  * @param {Object} config
34671  */
34672 Roo.tree.TreeSorter = function(tree, config){
34673     Roo.apply(this, config);
34674     tree.on("beforechildrenrendered", this.doSort, this);
34675     tree.on("append", this.updateSort, this);
34676     tree.on("insert", this.updateSort, this);
34677     
34678     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34679     var p = this.property || "text";
34680     var sortType = this.sortType;
34681     var fs = this.folderSort;
34682     var cs = this.caseSensitive === true;
34683     var leafAttr = this.leafAttr || 'leaf';
34684
34685     this.sortFn = function(n1, n2){
34686         if(fs){
34687             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34688                 return 1;
34689             }
34690             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34691                 return -1;
34692             }
34693         }
34694         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34695         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34696         if(v1 < v2){
34697                         return dsc ? +1 : -1;
34698                 }else if(v1 > v2){
34699                         return dsc ? -1 : +1;
34700         }else{
34701                 return 0;
34702         }
34703     };
34704 };
34705
34706 Roo.tree.TreeSorter.prototype = {
34707     doSort : function(node){
34708         node.sort(this.sortFn);
34709     },
34710     
34711     compareNodes : function(n1, n2){
34712         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34713     },
34714     
34715     updateSort : function(tree, node){
34716         if(node.childrenRendered){
34717             this.doSort.defer(1, this, [node]);
34718         }
34719     }
34720 };/*
34721  * Based on:
34722  * Ext JS Library 1.1.1
34723  * Copyright(c) 2006-2007, Ext JS, LLC.
34724  *
34725  * Originally Released Under LGPL - original licence link has changed is not relivant.
34726  *
34727  * Fork - LGPL
34728  * <script type="text/javascript">
34729  */
34730
34731 if(Roo.dd.DropZone){
34732     
34733 Roo.tree.TreeDropZone = function(tree, config){
34734     this.allowParentInsert = false;
34735     this.allowContainerDrop = false;
34736     this.appendOnly = false;
34737     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34738     this.tree = tree;
34739     this.lastInsertClass = "x-tree-no-status";
34740     this.dragOverData = {};
34741 };
34742
34743 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34744     ddGroup : "TreeDD",
34745     scroll:  true,
34746     
34747     expandDelay : 1000,
34748     
34749     expandNode : function(node){
34750         if(node.hasChildNodes() && !node.isExpanded()){
34751             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34752         }
34753     },
34754     
34755     queueExpand : function(node){
34756         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34757     },
34758     
34759     cancelExpand : function(){
34760         if(this.expandProcId){
34761             clearTimeout(this.expandProcId);
34762             this.expandProcId = false;
34763         }
34764     },
34765     
34766     isValidDropPoint : function(n, pt, dd, e, data){
34767         if(!n || !data){ return false; }
34768         var targetNode = n.node;
34769         var dropNode = data.node;
34770         // default drop rules
34771         if(!(targetNode && targetNode.isTarget && pt)){
34772             return false;
34773         }
34774         if(pt == "append" && targetNode.allowChildren === false){
34775             return false;
34776         }
34777         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34778             return false;
34779         }
34780         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34781             return false;
34782         }
34783         // reuse the object
34784         var overEvent = this.dragOverData;
34785         overEvent.tree = this.tree;
34786         overEvent.target = targetNode;
34787         overEvent.data = data;
34788         overEvent.point = pt;
34789         overEvent.source = dd;
34790         overEvent.rawEvent = e;
34791         overEvent.dropNode = dropNode;
34792         overEvent.cancel = false;  
34793         var result = this.tree.fireEvent("nodedragover", overEvent);
34794         return overEvent.cancel === false && result !== false;
34795     },
34796     
34797     getDropPoint : function(e, n, dd)
34798     {
34799         var tn = n.node;
34800         if(tn.isRoot){
34801             return tn.allowChildren !== false ? "append" : false; // always append for root
34802         }
34803         var dragEl = n.ddel;
34804         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34805         var y = Roo.lib.Event.getPageY(e);
34806         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34807         
34808         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34809         var noAppend = tn.allowChildren === false;
34810         if(this.appendOnly || tn.parentNode.allowChildren === false){
34811             return noAppend ? false : "append";
34812         }
34813         var noBelow = false;
34814         if(!this.allowParentInsert){
34815             noBelow = tn.hasChildNodes() && tn.isExpanded();
34816         }
34817         var q = (b - t) / (noAppend ? 2 : 3);
34818         if(y >= t && y < (t + q)){
34819             return "above";
34820         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34821             return "below";
34822         }else{
34823             return "append";
34824         }
34825     },
34826     
34827     onNodeEnter : function(n, dd, e, data)
34828     {
34829         this.cancelExpand();
34830     },
34831     
34832     onNodeOver : function(n, dd, e, data)
34833     {
34834        
34835         var pt = this.getDropPoint(e, n, dd);
34836         var node = n.node;
34837         
34838         // auto node expand check
34839         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34840             this.queueExpand(node);
34841         }else if(pt != "append"){
34842             this.cancelExpand();
34843         }
34844         
34845         // set the insert point style on the target node
34846         var returnCls = this.dropNotAllowed;
34847         if(this.isValidDropPoint(n, pt, dd, e, data)){
34848            if(pt){
34849                var el = n.ddel;
34850                var cls;
34851                if(pt == "above"){
34852                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34853                    cls = "x-tree-drag-insert-above";
34854                }else if(pt == "below"){
34855                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34856                    cls = "x-tree-drag-insert-below";
34857                }else{
34858                    returnCls = "x-tree-drop-ok-append";
34859                    cls = "x-tree-drag-append";
34860                }
34861                if(this.lastInsertClass != cls){
34862                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34863                    this.lastInsertClass = cls;
34864                }
34865            }
34866        }
34867        return returnCls;
34868     },
34869     
34870     onNodeOut : function(n, dd, e, data){
34871         
34872         this.cancelExpand();
34873         this.removeDropIndicators(n);
34874     },
34875     
34876     onNodeDrop : function(n, dd, e, data){
34877         var point = this.getDropPoint(e, n, dd);
34878         var targetNode = n.node;
34879         targetNode.ui.startDrop();
34880         if(!this.isValidDropPoint(n, point, dd, e, data)){
34881             targetNode.ui.endDrop();
34882             return false;
34883         }
34884         // first try to find the drop node
34885         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34886         var dropEvent = {
34887             tree : this.tree,
34888             target: targetNode,
34889             data: data,
34890             point: point,
34891             source: dd,
34892             rawEvent: e,
34893             dropNode: dropNode,
34894             cancel: !dropNode   
34895         };
34896         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34897         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34898             targetNode.ui.endDrop();
34899             return false;
34900         }
34901         // allow target changing
34902         targetNode = dropEvent.target;
34903         if(point == "append" && !targetNode.isExpanded()){
34904             targetNode.expand(false, null, function(){
34905                 this.completeDrop(dropEvent);
34906             }.createDelegate(this));
34907         }else{
34908             this.completeDrop(dropEvent);
34909         }
34910         return true;
34911     },
34912     
34913     completeDrop : function(de){
34914         var ns = de.dropNode, p = de.point, t = de.target;
34915         if(!(ns instanceof Array)){
34916             ns = [ns];
34917         }
34918         var n;
34919         for(var i = 0, len = ns.length; i < len; i++){
34920             n = ns[i];
34921             if(p == "above"){
34922                 t.parentNode.insertBefore(n, t);
34923             }else if(p == "below"){
34924                 t.parentNode.insertBefore(n, t.nextSibling);
34925             }else{
34926                 t.appendChild(n);
34927             }
34928         }
34929         n.ui.focus();
34930         if(this.tree.hlDrop){
34931             n.ui.highlight();
34932         }
34933         t.ui.endDrop();
34934         this.tree.fireEvent("nodedrop", de);
34935     },
34936     
34937     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34938         if(this.tree.hlDrop){
34939             dropNode.ui.focus();
34940             dropNode.ui.highlight();
34941         }
34942         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34943     },
34944     
34945     getTree : function(){
34946         return this.tree;
34947     },
34948     
34949     removeDropIndicators : function(n){
34950         if(n && n.ddel){
34951             var el = n.ddel;
34952             Roo.fly(el).removeClass([
34953                     "x-tree-drag-insert-above",
34954                     "x-tree-drag-insert-below",
34955                     "x-tree-drag-append"]);
34956             this.lastInsertClass = "_noclass";
34957         }
34958     },
34959     
34960     beforeDragDrop : function(target, e, id){
34961         this.cancelExpand();
34962         return true;
34963     },
34964     
34965     afterRepair : function(data){
34966         if(data && Roo.enableFx){
34967             data.node.ui.highlight();
34968         }
34969         this.hideProxy();
34970     } 
34971     
34972 });
34973
34974 }
34975 /*
34976  * Based on:
34977  * Ext JS Library 1.1.1
34978  * Copyright(c) 2006-2007, Ext JS, LLC.
34979  *
34980  * Originally Released Under LGPL - original licence link has changed is not relivant.
34981  *
34982  * Fork - LGPL
34983  * <script type="text/javascript">
34984  */
34985  
34986
34987 if(Roo.dd.DragZone){
34988 Roo.tree.TreeDragZone = function(tree, config){
34989     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34990     this.tree = tree;
34991 };
34992
34993 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34994     ddGroup : "TreeDD",
34995    
34996     onBeforeDrag : function(data, e){
34997         var n = data.node;
34998         return n && n.draggable && !n.disabled;
34999     },
35000      
35001     
35002     onInitDrag : function(e){
35003         var data = this.dragData;
35004         this.tree.getSelectionModel().select(data.node);
35005         this.proxy.update("");
35006         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35007         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35008     },
35009     
35010     getRepairXY : function(e, data){
35011         return data.node.ui.getDDRepairXY();
35012     },
35013     
35014     onEndDrag : function(data, e){
35015         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35016         
35017         
35018     },
35019     
35020     onValidDrop : function(dd, e, id){
35021         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35022         this.hideProxy();
35023     },
35024     
35025     beforeInvalidDrop : function(e, id){
35026         // this scrolls the original position back into view
35027         var sm = this.tree.getSelectionModel();
35028         sm.clearSelections();
35029         sm.select(this.dragData.node);
35030     }
35031 });
35032 }/*
35033  * Based on:
35034  * Ext JS Library 1.1.1
35035  * Copyright(c) 2006-2007, Ext JS, LLC.
35036  *
35037  * Originally Released Under LGPL - original licence link has changed is not relivant.
35038  *
35039  * Fork - LGPL
35040  * <script type="text/javascript">
35041  */
35042 /**
35043  * @class Roo.tree.TreeEditor
35044  * @extends Roo.Editor
35045  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35046  * as the editor field.
35047  * @constructor
35048  * @param {Object} config (used to be the tree panel.)
35049  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35050  * 
35051  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35052  * @cfg {Roo.form.TextField|Object} field The field configuration
35053  *
35054  * 
35055  */
35056 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35057     var tree = config;
35058     var field;
35059     if (oldconfig) { // old style..
35060         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35061     } else {
35062         // new style..
35063         tree = config.tree;
35064         config.field = config.field  || {};
35065         config.field.xtype = 'TextField';
35066         field = Roo.factory(config.field, Roo.form);
35067     }
35068     config = config || {};
35069     
35070     
35071     this.addEvents({
35072         /**
35073          * @event beforenodeedit
35074          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35075          * false from the handler of this event.
35076          * @param {Editor} this
35077          * @param {Roo.tree.Node} node 
35078          */
35079         "beforenodeedit" : true
35080     });
35081     
35082     //Roo.log(config);
35083     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35084
35085     this.tree = tree;
35086
35087     tree.on('beforeclick', this.beforeNodeClick, this);
35088     tree.getTreeEl().on('mousedown', this.hide, this);
35089     this.on('complete', this.updateNode, this);
35090     this.on('beforestartedit', this.fitToTree, this);
35091     this.on('startedit', this.bindScroll, this, {delay:10});
35092     this.on('specialkey', this.onSpecialKey, this);
35093 };
35094
35095 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35096     /**
35097      * @cfg {String} alignment
35098      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35099      */
35100     alignment: "l-l",
35101     // inherit
35102     autoSize: false,
35103     /**
35104      * @cfg {Boolean} hideEl
35105      * True to hide the bound element while the editor is displayed (defaults to false)
35106      */
35107     hideEl : false,
35108     /**
35109      * @cfg {String} cls
35110      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35111      */
35112     cls: "x-small-editor x-tree-editor",
35113     /**
35114      * @cfg {Boolean} shim
35115      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35116      */
35117     shim:false,
35118     // inherit
35119     shadow:"frame",
35120     /**
35121      * @cfg {Number} maxWidth
35122      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35123      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35124      * scroll and client offsets into account prior to each edit.
35125      */
35126     maxWidth: 250,
35127
35128     editDelay : 350,
35129
35130     // private
35131     fitToTree : function(ed, el){
35132         var td = this.tree.getTreeEl().dom, nd = el.dom;
35133         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35134             td.scrollLeft = nd.offsetLeft;
35135         }
35136         var w = Math.min(
35137                 this.maxWidth,
35138                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35139         this.setSize(w, '');
35140         
35141         return this.fireEvent('beforenodeedit', this, this.editNode);
35142         
35143     },
35144
35145     // private
35146     triggerEdit : function(node){
35147         this.completeEdit();
35148         this.editNode = node;
35149         this.startEdit(node.ui.textNode, node.text);
35150     },
35151
35152     // private
35153     bindScroll : function(){
35154         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35155     },
35156
35157     // private
35158     beforeNodeClick : function(node, e){
35159         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35160         this.lastClick = new Date();
35161         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35162             e.stopEvent();
35163             this.triggerEdit(node);
35164             return false;
35165         }
35166         return true;
35167     },
35168
35169     // private
35170     updateNode : function(ed, value){
35171         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35172         this.editNode.setText(value);
35173     },
35174
35175     // private
35176     onHide : function(){
35177         Roo.tree.TreeEditor.superclass.onHide.call(this);
35178         if(this.editNode){
35179             this.editNode.ui.focus();
35180         }
35181     },
35182
35183     // private
35184     onSpecialKey : function(field, e){
35185         var k = e.getKey();
35186         if(k == e.ESC){
35187             e.stopEvent();
35188             this.cancelEdit();
35189         }else if(k == e.ENTER && !e.hasModifier()){
35190             e.stopEvent();
35191             this.completeEdit();
35192         }
35193     }
35194 });//<Script type="text/javascript">
35195 /*
35196  * Based on:
35197  * Ext JS Library 1.1.1
35198  * Copyright(c) 2006-2007, Ext JS, LLC.
35199  *
35200  * Originally Released Under LGPL - original licence link has changed is not relivant.
35201  *
35202  * Fork - LGPL
35203  * <script type="text/javascript">
35204  */
35205  
35206 /**
35207  * Not documented??? - probably should be...
35208  */
35209
35210 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35211     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35212     
35213     renderElements : function(n, a, targetNode, bulkRender){
35214         //consel.log("renderElements?");
35215         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35216
35217         var t = n.getOwnerTree();
35218         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35219         
35220         var cols = t.columns;
35221         var bw = t.borderWidth;
35222         var c = cols[0];
35223         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35224          var cb = typeof a.checked == "boolean";
35225         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35226         var colcls = 'x-t-' + tid + '-c0';
35227         var buf = [
35228             '<li class="x-tree-node">',
35229             
35230                 
35231                 '<div class="x-tree-node-el ', a.cls,'">',
35232                     // extran...
35233                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35234                 
35235                 
35236                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35237                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35238                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35239                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35240                            (a.iconCls ? ' '+a.iconCls : ''),
35241                            '" unselectable="on" />',
35242                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35243                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35244                              
35245                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35246                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35247                             '<span unselectable="on" qtip="' + tx + '">',
35248                              tx,
35249                              '</span></a>' ,
35250                     '</div>',
35251                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35252                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35253                  ];
35254         for(var i = 1, len = cols.length; i < len; i++){
35255             c = cols[i];
35256             colcls = 'x-t-' + tid + '-c' +i;
35257             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35258             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35259                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35260                       "</div>");
35261          }
35262          
35263          buf.push(
35264             '</a>',
35265             '<div class="x-clear"></div></div>',
35266             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35267             "</li>");
35268         
35269         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35270             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35271                                 n.nextSibling.ui.getEl(), buf.join(""));
35272         }else{
35273             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35274         }
35275         var el = this.wrap.firstChild;
35276         this.elRow = el;
35277         this.elNode = el.firstChild;
35278         this.ranchor = el.childNodes[1];
35279         this.ctNode = this.wrap.childNodes[1];
35280         var cs = el.firstChild.childNodes;
35281         this.indentNode = cs[0];
35282         this.ecNode = cs[1];
35283         this.iconNode = cs[2];
35284         var index = 3;
35285         if(cb){
35286             this.checkbox = cs[3];
35287             index++;
35288         }
35289         this.anchor = cs[index];
35290         
35291         this.textNode = cs[index].firstChild;
35292         
35293         //el.on("click", this.onClick, this);
35294         //el.on("dblclick", this.onDblClick, this);
35295         
35296         
35297        // console.log(this);
35298     },
35299     initEvents : function(){
35300         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35301         
35302             
35303         var a = this.ranchor;
35304
35305         var el = Roo.get(a);
35306
35307         if(Roo.isOpera){ // opera render bug ignores the CSS
35308             el.setStyle("text-decoration", "none");
35309         }
35310
35311         el.on("click", this.onClick, this);
35312         el.on("dblclick", this.onDblClick, this);
35313         el.on("contextmenu", this.onContextMenu, this);
35314         
35315     },
35316     
35317     /*onSelectedChange : function(state){
35318         if(state){
35319             this.focus();
35320             this.addClass("x-tree-selected");
35321         }else{
35322             //this.blur();
35323             this.removeClass("x-tree-selected");
35324         }
35325     },*/
35326     addClass : function(cls){
35327         if(this.elRow){
35328             Roo.fly(this.elRow).addClass(cls);
35329         }
35330         
35331     },
35332     
35333     
35334     removeClass : function(cls){
35335         if(this.elRow){
35336             Roo.fly(this.elRow).removeClass(cls);
35337         }
35338     }
35339
35340     
35341     
35342 });//<Script type="text/javascript">
35343
35344 /*
35345  * Based on:
35346  * Ext JS Library 1.1.1
35347  * Copyright(c) 2006-2007, Ext JS, LLC.
35348  *
35349  * Originally Released Under LGPL - original licence link has changed is not relivant.
35350  *
35351  * Fork - LGPL
35352  * <script type="text/javascript">
35353  */
35354  
35355
35356 /**
35357  * @class Roo.tree.ColumnTree
35358  * @extends Roo.data.TreePanel
35359  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35360  * @cfg {int} borderWidth  compined right/left border allowance
35361  * @constructor
35362  * @param {String/HTMLElement/Element} el The container element
35363  * @param {Object} config
35364  */
35365 Roo.tree.ColumnTree =  function(el, config)
35366 {
35367    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35368    this.addEvents({
35369         /**
35370         * @event resize
35371         * Fire this event on a container when it resizes
35372         * @param {int} w Width
35373         * @param {int} h Height
35374         */
35375        "resize" : true
35376     });
35377     this.on('resize', this.onResize, this);
35378 };
35379
35380 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35381     //lines:false,
35382     
35383     
35384     borderWidth: Roo.isBorderBox ? 0 : 2, 
35385     headEls : false,
35386     
35387     render : function(){
35388         // add the header.....
35389        
35390         Roo.tree.ColumnTree.superclass.render.apply(this);
35391         
35392         this.el.addClass('x-column-tree');
35393         
35394         this.headers = this.el.createChild(
35395             {cls:'x-tree-headers'},this.innerCt.dom);
35396    
35397         var cols = this.columns, c;
35398         var totalWidth = 0;
35399         this.headEls = [];
35400         var  len = cols.length;
35401         for(var i = 0; i < len; i++){
35402              c = cols[i];
35403              totalWidth += c.width;
35404             this.headEls.push(this.headers.createChild({
35405                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35406                  cn: {
35407                      cls:'x-tree-hd-text',
35408                      html: c.header
35409                  },
35410                  style:'width:'+(c.width-this.borderWidth)+'px;'
35411              }));
35412         }
35413         this.headers.createChild({cls:'x-clear'});
35414         // prevent floats from wrapping when clipped
35415         this.headers.setWidth(totalWidth);
35416         //this.innerCt.setWidth(totalWidth);
35417         this.innerCt.setStyle({ overflow: 'auto' });
35418         this.onResize(this.width, this.height);
35419              
35420         
35421     },
35422     onResize : function(w,h)
35423     {
35424         this.height = h;
35425         this.width = w;
35426         // resize cols..
35427         this.innerCt.setWidth(this.width);
35428         this.innerCt.setHeight(this.height-20);
35429         
35430         // headers...
35431         var cols = this.columns, c;
35432         var totalWidth = 0;
35433         var expEl = false;
35434         var len = cols.length;
35435         for(var i = 0; i < len; i++){
35436             c = cols[i];
35437             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35438                 // it's the expander..
35439                 expEl  = this.headEls[i];
35440                 continue;
35441             }
35442             totalWidth += c.width;
35443             
35444         }
35445         if (expEl) {
35446             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35447         }
35448         this.headers.setWidth(w-20);
35449
35450         
35451         
35452         
35453     }
35454 });
35455 /*
35456  * Based on:
35457  * Ext JS Library 1.1.1
35458  * Copyright(c) 2006-2007, Ext JS, LLC.
35459  *
35460  * Originally Released Under LGPL - original licence link has changed is not relivant.
35461  *
35462  * Fork - LGPL
35463  * <script type="text/javascript">
35464  */
35465  
35466 /**
35467  * @class Roo.menu.Menu
35468  * @extends Roo.util.Observable
35469  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35470  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35471  * @constructor
35472  * Creates a new Menu
35473  * @param {Object} config Configuration options
35474  */
35475 Roo.menu.Menu = function(config){
35476     Roo.apply(this, config);
35477     this.id = this.id || Roo.id();
35478     this.addEvents({
35479         /**
35480          * @event beforeshow
35481          * Fires before this menu is displayed
35482          * @param {Roo.menu.Menu} this
35483          */
35484         beforeshow : true,
35485         /**
35486          * @event beforehide
35487          * Fires before this menu is hidden
35488          * @param {Roo.menu.Menu} this
35489          */
35490         beforehide : true,
35491         /**
35492          * @event show
35493          * Fires after this menu is displayed
35494          * @param {Roo.menu.Menu} this
35495          */
35496         show : true,
35497         /**
35498          * @event hide
35499          * Fires after this menu is hidden
35500          * @param {Roo.menu.Menu} this
35501          */
35502         hide : true,
35503         /**
35504          * @event click
35505          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35506          * @param {Roo.menu.Menu} this
35507          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35508          * @param {Roo.EventObject} e
35509          */
35510         click : true,
35511         /**
35512          * @event mouseover
35513          * Fires when the mouse is hovering over this menu
35514          * @param {Roo.menu.Menu} this
35515          * @param {Roo.EventObject} e
35516          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35517          */
35518         mouseover : true,
35519         /**
35520          * @event mouseout
35521          * Fires when the mouse exits this menu
35522          * @param {Roo.menu.Menu} this
35523          * @param {Roo.EventObject} e
35524          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35525          */
35526         mouseout : true,
35527         /**
35528          * @event itemclick
35529          * Fires when a menu item contained in this menu is clicked
35530          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35531          * @param {Roo.EventObject} e
35532          */
35533         itemclick: true
35534     });
35535     if (this.registerMenu) {
35536         Roo.menu.MenuMgr.register(this);
35537     }
35538     
35539     var mis = this.items;
35540     this.items = new Roo.util.MixedCollection();
35541     if(mis){
35542         this.add.apply(this, mis);
35543     }
35544 };
35545
35546 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35547     /**
35548      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35549      */
35550     minWidth : 120,
35551     /**
35552      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35553      * for bottom-right shadow (defaults to "sides")
35554      */
35555     shadow : "sides",
35556     /**
35557      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35558      * this menu (defaults to "tl-tr?")
35559      */
35560     subMenuAlign : "tl-tr?",
35561     /**
35562      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35563      * relative to its element of origin (defaults to "tl-bl?")
35564      */
35565     defaultAlign : "tl-bl?",
35566     /**
35567      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35568      */
35569     allowOtherMenus : false,
35570     /**
35571      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35572      */
35573     registerMenu : true,
35574
35575     hidden:true,
35576
35577     // private
35578     render : function(){
35579         if(this.el){
35580             return;
35581         }
35582         var el = this.el = new Roo.Layer({
35583             cls: "x-menu",
35584             shadow:this.shadow,
35585             constrain: false,
35586             parentEl: this.parentEl || document.body,
35587             zindex:15000
35588         });
35589
35590         this.keyNav = new Roo.menu.MenuNav(this);
35591
35592         if(this.plain){
35593             el.addClass("x-menu-plain");
35594         }
35595         if(this.cls){
35596             el.addClass(this.cls);
35597         }
35598         // generic focus element
35599         this.focusEl = el.createChild({
35600             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35601         });
35602         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35603         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35604         
35605         ul.on("mouseover", this.onMouseOver, this);
35606         ul.on("mouseout", this.onMouseOut, this);
35607         this.items.each(function(item){
35608             if (item.hidden) {
35609                 return;
35610             }
35611             
35612             var li = document.createElement("li");
35613             li.className = "x-menu-list-item";
35614             ul.dom.appendChild(li);
35615             item.render(li, this);
35616         }, this);
35617         this.ul = ul;
35618         this.autoWidth();
35619     },
35620
35621     // private
35622     autoWidth : function(){
35623         var el = this.el, ul = this.ul;
35624         if(!el){
35625             return;
35626         }
35627         var w = this.width;
35628         if(w){
35629             el.setWidth(w);
35630         }else if(Roo.isIE){
35631             el.setWidth(this.minWidth);
35632             var t = el.dom.offsetWidth; // force recalc
35633             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35634         }
35635     },
35636
35637     // private
35638     delayAutoWidth : function(){
35639         if(this.rendered){
35640             if(!this.awTask){
35641                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35642             }
35643             this.awTask.delay(20);
35644         }
35645     },
35646
35647     // private
35648     findTargetItem : function(e){
35649         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35650         if(t && t.menuItemId){
35651             return this.items.get(t.menuItemId);
35652         }
35653     },
35654
35655     // private
35656     onClick : function(e){
35657         Roo.log("menu.onClick");
35658         var t = this.findTargetItem(e);
35659         if(!t){
35660             return;
35661         }
35662         Roo.log(e);
35663         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35664             if(t == this.activeItem && t.shouldDeactivate(e)){
35665                 this.activeItem.deactivate();
35666                 delete this.activeItem;
35667                 return;
35668             }
35669             if(t.canActivate){
35670                 this.setActiveItem(t, true);
35671             }
35672             return;
35673             
35674             
35675         }
35676         
35677         t.onClick(e);
35678         this.fireEvent("click", this, t, e);
35679     },
35680
35681     // private
35682     setActiveItem : function(item, autoExpand){
35683         if(item != this.activeItem){
35684             if(this.activeItem){
35685                 this.activeItem.deactivate();
35686             }
35687             this.activeItem = item;
35688             item.activate(autoExpand);
35689         }else if(autoExpand){
35690             item.expandMenu();
35691         }
35692     },
35693
35694     // private
35695     tryActivate : function(start, step){
35696         var items = this.items;
35697         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35698             var item = items.get(i);
35699             if(!item.disabled && item.canActivate){
35700                 this.setActiveItem(item, false);
35701                 return item;
35702             }
35703         }
35704         return false;
35705     },
35706
35707     // private
35708     onMouseOver : function(e){
35709         var t;
35710         if(t = this.findTargetItem(e)){
35711             if(t.canActivate && !t.disabled){
35712                 this.setActiveItem(t, true);
35713             }
35714         }
35715         this.fireEvent("mouseover", this, e, t);
35716     },
35717
35718     // private
35719     onMouseOut : function(e){
35720         var t;
35721         if(t = this.findTargetItem(e)){
35722             if(t == this.activeItem && t.shouldDeactivate(e)){
35723                 this.activeItem.deactivate();
35724                 delete this.activeItem;
35725             }
35726         }
35727         this.fireEvent("mouseout", this, e, t);
35728     },
35729
35730     /**
35731      * Read-only.  Returns true if the menu is currently displayed, else false.
35732      * @type Boolean
35733      */
35734     isVisible : function(){
35735         return this.el && !this.hidden;
35736     },
35737
35738     /**
35739      * Displays this menu relative to another element
35740      * @param {String/HTMLElement/Roo.Element} element The element to align to
35741      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35742      * the element (defaults to this.defaultAlign)
35743      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35744      */
35745     show : function(el, pos, parentMenu){
35746         this.parentMenu = parentMenu;
35747         if(!this.el){
35748             this.render();
35749         }
35750         this.fireEvent("beforeshow", this);
35751         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35752     },
35753
35754     /**
35755      * Displays this menu at a specific xy position
35756      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35757      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35758      */
35759     showAt : function(xy, parentMenu, /* private: */_e){
35760         this.parentMenu = parentMenu;
35761         if(!this.el){
35762             this.render();
35763         }
35764         if(_e !== false){
35765             this.fireEvent("beforeshow", this);
35766             xy = this.el.adjustForConstraints(xy);
35767         }
35768         this.el.setXY(xy);
35769         this.el.show();
35770         this.hidden = false;
35771         this.focus();
35772         this.fireEvent("show", this);
35773     },
35774
35775     focus : function(){
35776         if(!this.hidden){
35777             this.doFocus.defer(50, this);
35778         }
35779     },
35780
35781     doFocus : function(){
35782         if(!this.hidden){
35783             this.focusEl.focus();
35784         }
35785     },
35786
35787     /**
35788      * Hides this menu and optionally all parent menus
35789      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35790      */
35791     hide : function(deep){
35792         if(this.el && this.isVisible()){
35793             this.fireEvent("beforehide", this);
35794             if(this.activeItem){
35795                 this.activeItem.deactivate();
35796                 this.activeItem = null;
35797             }
35798             this.el.hide();
35799             this.hidden = true;
35800             this.fireEvent("hide", this);
35801         }
35802         if(deep === true && this.parentMenu){
35803             this.parentMenu.hide(true);
35804         }
35805     },
35806
35807     /**
35808      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35809      * Any of the following are valid:
35810      * <ul>
35811      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35812      * <li>An HTMLElement object which will be converted to a menu item</li>
35813      * <li>A menu item config object that will be created as a new menu item</li>
35814      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35815      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35816      * </ul>
35817      * Usage:
35818      * <pre><code>
35819 // Create the menu
35820 var menu = new Roo.menu.Menu();
35821
35822 // Create a menu item to add by reference
35823 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35824
35825 // Add a bunch of items at once using different methods.
35826 // Only the last item added will be returned.
35827 var item = menu.add(
35828     menuItem,                // add existing item by ref
35829     'Dynamic Item',          // new TextItem
35830     '-',                     // new separator
35831     { text: 'Config Item' }  // new item by config
35832 );
35833 </code></pre>
35834      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35835      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35836      */
35837     add : function(){
35838         var a = arguments, l = a.length, item;
35839         for(var i = 0; i < l; i++){
35840             var el = a[i];
35841             if ((typeof(el) == "object") && el.xtype && el.xns) {
35842                 el = Roo.factory(el, Roo.menu);
35843             }
35844             
35845             if(el.render){ // some kind of Item
35846                 item = this.addItem(el);
35847             }else if(typeof el == "string"){ // string
35848                 if(el == "separator" || el == "-"){
35849                     item = this.addSeparator();
35850                 }else{
35851                     item = this.addText(el);
35852                 }
35853             }else if(el.tagName || el.el){ // element
35854                 item = this.addElement(el);
35855             }else if(typeof el == "object"){ // must be menu item config?
35856                 item = this.addMenuItem(el);
35857             }
35858         }
35859         return item;
35860     },
35861
35862     /**
35863      * Returns this menu's underlying {@link Roo.Element} object
35864      * @return {Roo.Element} The element
35865      */
35866     getEl : function(){
35867         if(!this.el){
35868             this.render();
35869         }
35870         return this.el;
35871     },
35872
35873     /**
35874      * Adds a separator bar to the menu
35875      * @return {Roo.menu.Item} The menu item that was added
35876      */
35877     addSeparator : function(){
35878         return this.addItem(new Roo.menu.Separator());
35879     },
35880
35881     /**
35882      * Adds an {@link Roo.Element} object to the menu
35883      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35884      * @return {Roo.menu.Item} The menu item that was added
35885      */
35886     addElement : function(el){
35887         return this.addItem(new Roo.menu.BaseItem(el));
35888     },
35889
35890     /**
35891      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35892      * @param {Roo.menu.Item} item The menu item to add
35893      * @return {Roo.menu.Item} The menu item that was added
35894      */
35895     addItem : function(item){
35896         this.items.add(item);
35897         if(this.ul){
35898             var li = document.createElement("li");
35899             li.className = "x-menu-list-item";
35900             this.ul.dom.appendChild(li);
35901             item.render(li, this);
35902             this.delayAutoWidth();
35903         }
35904         return item;
35905     },
35906
35907     /**
35908      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35909      * @param {Object} config A MenuItem config object
35910      * @return {Roo.menu.Item} The menu item that was added
35911      */
35912     addMenuItem : function(config){
35913         if(!(config instanceof Roo.menu.Item)){
35914             if(typeof config.checked == "boolean"){ // must be check menu item config?
35915                 config = new Roo.menu.CheckItem(config);
35916             }else{
35917                 config = new Roo.menu.Item(config);
35918             }
35919         }
35920         return this.addItem(config);
35921     },
35922
35923     /**
35924      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35925      * @param {String} text The text to display in the menu item
35926      * @return {Roo.menu.Item} The menu item that was added
35927      */
35928     addText : function(text){
35929         return this.addItem(new Roo.menu.TextItem({ text : text }));
35930     },
35931
35932     /**
35933      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35934      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35935      * @param {Roo.menu.Item} item The menu item to add
35936      * @return {Roo.menu.Item} The menu item that was added
35937      */
35938     insert : function(index, item){
35939         this.items.insert(index, item);
35940         if(this.ul){
35941             var li = document.createElement("li");
35942             li.className = "x-menu-list-item";
35943             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35944             item.render(li, this);
35945             this.delayAutoWidth();
35946         }
35947         return item;
35948     },
35949
35950     /**
35951      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35952      * @param {Roo.menu.Item} item The menu item to remove
35953      */
35954     remove : function(item){
35955         this.items.removeKey(item.id);
35956         item.destroy();
35957     },
35958
35959     /**
35960      * Removes and destroys all items in the menu
35961      */
35962     removeAll : function(){
35963         var f;
35964         while(f = this.items.first()){
35965             this.remove(f);
35966         }
35967     }
35968 });
35969
35970 // MenuNav is a private utility class used internally by the Menu
35971 Roo.menu.MenuNav = function(menu){
35972     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35973     this.scope = this.menu = menu;
35974 };
35975
35976 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35977     doRelay : function(e, h){
35978         var k = e.getKey();
35979         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35980             this.menu.tryActivate(0, 1);
35981             return false;
35982         }
35983         return h.call(this.scope || this, e, this.menu);
35984     },
35985
35986     up : function(e, m){
35987         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35988             m.tryActivate(m.items.length-1, -1);
35989         }
35990     },
35991
35992     down : function(e, m){
35993         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35994             m.tryActivate(0, 1);
35995         }
35996     },
35997
35998     right : function(e, m){
35999         if(m.activeItem){
36000             m.activeItem.expandMenu(true);
36001         }
36002     },
36003
36004     left : function(e, m){
36005         m.hide();
36006         if(m.parentMenu && m.parentMenu.activeItem){
36007             m.parentMenu.activeItem.activate();
36008         }
36009     },
36010
36011     enter : function(e, m){
36012         if(m.activeItem){
36013             e.stopPropagation();
36014             m.activeItem.onClick(e);
36015             m.fireEvent("click", this, m.activeItem);
36016             return true;
36017         }
36018     }
36019 });/*
36020  * Based on:
36021  * Ext JS Library 1.1.1
36022  * Copyright(c) 2006-2007, Ext JS, LLC.
36023  *
36024  * Originally Released Under LGPL - original licence link has changed is not relivant.
36025  *
36026  * Fork - LGPL
36027  * <script type="text/javascript">
36028  */
36029  
36030 /**
36031  * @class Roo.menu.MenuMgr
36032  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36033  * @singleton
36034  */
36035 Roo.menu.MenuMgr = function(){
36036    var menus, active, groups = {}, attached = false, lastShow = new Date();
36037
36038    // private - called when first menu is created
36039    function init(){
36040        menus = {};
36041        active = new Roo.util.MixedCollection();
36042        Roo.get(document).addKeyListener(27, function(){
36043            if(active.length > 0){
36044                hideAll();
36045            }
36046        });
36047    }
36048
36049    // private
36050    function hideAll(){
36051        if(active && active.length > 0){
36052            var c = active.clone();
36053            c.each(function(m){
36054                m.hide();
36055            });
36056        }
36057    }
36058
36059    // private
36060    function onHide(m){
36061        active.remove(m);
36062        if(active.length < 1){
36063            Roo.get(document).un("mousedown", onMouseDown);
36064            attached = false;
36065        }
36066    }
36067
36068    // private
36069    function onShow(m){
36070        var last = active.last();
36071        lastShow = new Date();
36072        active.add(m);
36073        if(!attached){
36074            Roo.get(document).on("mousedown", onMouseDown);
36075            attached = true;
36076        }
36077        if(m.parentMenu){
36078           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36079           m.parentMenu.activeChild = m;
36080        }else if(last && last.isVisible()){
36081           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36082        }
36083    }
36084
36085    // private
36086    function onBeforeHide(m){
36087        if(m.activeChild){
36088            m.activeChild.hide();
36089        }
36090        if(m.autoHideTimer){
36091            clearTimeout(m.autoHideTimer);
36092            delete m.autoHideTimer;
36093        }
36094    }
36095
36096    // private
36097    function onBeforeShow(m){
36098        var pm = m.parentMenu;
36099        if(!pm && !m.allowOtherMenus){
36100            hideAll();
36101        }else if(pm && pm.activeChild && active != m){
36102            pm.activeChild.hide();
36103        }
36104    }
36105
36106    // private
36107    function onMouseDown(e){
36108        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36109            hideAll();
36110        }
36111    }
36112
36113    // private
36114    function onBeforeCheck(mi, state){
36115        if(state){
36116            var g = groups[mi.group];
36117            for(var i = 0, l = g.length; i < l; i++){
36118                if(g[i] != mi){
36119                    g[i].setChecked(false);
36120                }
36121            }
36122        }
36123    }
36124
36125    return {
36126
36127        /**
36128         * Hides all menus that are currently visible
36129         */
36130        hideAll : function(){
36131             hideAll();  
36132        },
36133
36134        // private
36135        register : function(menu){
36136            if(!menus){
36137                init();
36138            }
36139            menus[menu.id] = menu;
36140            menu.on("beforehide", onBeforeHide);
36141            menu.on("hide", onHide);
36142            menu.on("beforeshow", onBeforeShow);
36143            menu.on("show", onShow);
36144            var g = menu.group;
36145            if(g && menu.events["checkchange"]){
36146                if(!groups[g]){
36147                    groups[g] = [];
36148                }
36149                groups[g].push(menu);
36150                menu.on("checkchange", onCheck);
36151            }
36152        },
36153
36154         /**
36155          * Returns a {@link Roo.menu.Menu} object
36156          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36157          * be used to generate and return a new Menu instance.
36158          */
36159        get : function(menu){
36160            if(typeof menu == "string"){ // menu id
36161                return menus[menu];
36162            }else if(menu.events){  // menu instance
36163                return menu;
36164            }else if(typeof menu.length == 'number'){ // array of menu items?
36165                return new Roo.menu.Menu({items:menu});
36166            }else{ // otherwise, must be a config
36167                return new Roo.menu.Menu(menu);
36168            }
36169        },
36170
36171        // private
36172        unregister : function(menu){
36173            delete menus[menu.id];
36174            menu.un("beforehide", onBeforeHide);
36175            menu.un("hide", onHide);
36176            menu.un("beforeshow", onBeforeShow);
36177            menu.un("show", onShow);
36178            var g = menu.group;
36179            if(g && menu.events["checkchange"]){
36180                groups[g].remove(menu);
36181                menu.un("checkchange", onCheck);
36182            }
36183        },
36184
36185        // private
36186        registerCheckable : function(menuItem){
36187            var g = menuItem.group;
36188            if(g){
36189                if(!groups[g]){
36190                    groups[g] = [];
36191                }
36192                groups[g].push(menuItem);
36193                menuItem.on("beforecheckchange", onBeforeCheck);
36194            }
36195        },
36196
36197        // private
36198        unregisterCheckable : function(menuItem){
36199            var g = menuItem.group;
36200            if(g){
36201                groups[g].remove(menuItem);
36202                menuItem.un("beforecheckchange", onBeforeCheck);
36203            }
36204        }
36205    };
36206 }();/*
36207  * Based on:
36208  * Ext JS Library 1.1.1
36209  * Copyright(c) 2006-2007, Ext JS, LLC.
36210  *
36211  * Originally Released Under LGPL - original licence link has changed is not relivant.
36212  *
36213  * Fork - LGPL
36214  * <script type="text/javascript">
36215  */
36216  
36217
36218 /**
36219  * @class Roo.menu.BaseItem
36220  * @extends Roo.Component
36221  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36222  * management and base configuration options shared by all menu components.
36223  * @constructor
36224  * Creates a new BaseItem
36225  * @param {Object} config Configuration options
36226  */
36227 Roo.menu.BaseItem = function(config){
36228     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36229
36230     this.addEvents({
36231         /**
36232          * @event click
36233          * Fires when this item is clicked
36234          * @param {Roo.menu.BaseItem} this
36235          * @param {Roo.EventObject} e
36236          */
36237         click: true,
36238         /**
36239          * @event activate
36240          * Fires when this item is activated
36241          * @param {Roo.menu.BaseItem} this
36242          */
36243         activate : true,
36244         /**
36245          * @event deactivate
36246          * Fires when this item is deactivated
36247          * @param {Roo.menu.BaseItem} this
36248          */
36249         deactivate : true
36250     });
36251
36252     if(this.handler){
36253         this.on("click", this.handler, this.scope, true);
36254     }
36255 };
36256
36257 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36258     /**
36259      * @cfg {Function} handler
36260      * A function that will handle the click event of this menu item (defaults to undefined)
36261      */
36262     /**
36263      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36264      */
36265     canActivate : false,
36266     
36267      /**
36268      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36269      */
36270     hidden: false,
36271     
36272     /**
36273      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36274      */
36275     activeClass : "x-menu-item-active",
36276     /**
36277      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36278      */
36279     hideOnClick : true,
36280     /**
36281      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36282      */
36283     hideDelay : 100,
36284
36285     // private
36286     ctype: "Roo.menu.BaseItem",
36287
36288     // private
36289     actionMode : "container",
36290
36291     // private
36292     render : function(container, parentMenu){
36293         this.parentMenu = parentMenu;
36294         Roo.menu.BaseItem.superclass.render.call(this, container);
36295         this.container.menuItemId = this.id;
36296     },
36297
36298     // private
36299     onRender : function(container, position){
36300         this.el = Roo.get(this.el);
36301         container.dom.appendChild(this.el.dom);
36302     },
36303
36304     // private
36305     onClick : function(e){
36306         if(!this.disabled && this.fireEvent("click", this, e) !== false
36307                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36308             this.handleClick(e);
36309         }else{
36310             e.stopEvent();
36311         }
36312     },
36313
36314     // private
36315     activate : function(){
36316         if(this.disabled){
36317             return false;
36318         }
36319         var li = this.container;
36320         li.addClass(this.activeClass);
36321         this.region = li.getRegion().adjust(2, 2, -2, -2);
36322         this.fireEvent("activate", this);
36323         return true;
36324     },
36325
36326     // private
36327     deactivate : function(){
36328         this.container.removeClass(this.activeClass);
36329         this.fireEvent("deactivate", this);
36330     },
36331
36332     // private
36333     shouldDeactivate : function(e){
36334         return !this.region || !this.region.contains(e.getPoint());
36335     },
36336
36337     // private
36338     handleClick : function(e){
36339         if(this.hideOnClick){
36340             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36341         }
36342     },
36343
36344     // private
36345     expandMenu : function(autoActivate){
36346         // do nothing
36347     },
36348
36349     // private
36350     hideMenu : function(){
36351         // do nothing
36352     }
36353 });/*
36354  * Based on:
36355  * Ext JS Library 1.1.1
36356  * Copyright(c) 2006-2007, Ext JS, LLC.
36357  *
36358  * Originally Released Under LGPL - original licence link has changed is not relivant.
36359  *
36360  * Fork - LGPL
36361  * <script type="text/javascript">
36362  */
36363  
36364 /**
36365  * @class Roo.menu.Adapter
36366  * @extends Roo.menu.BaseItem
36367  * 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.
36368  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36369  * @constructor
36370  * Creates a new Adapter
36371  * @param {Object} config Configuration options
36372  */
36373 Roo.menu.Adapter = function(component, config){
36374     Roo.menu.Adapter.superclass.constructor.call(this, config);
36375     this.component = component;
36376 };
36377 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36378     // private
36379     canActivate : true,
36380
36381     // private
36382     onRender : function(container, position){
36383         this.component.render(container);
36384         this.el = this.component.getEl();
36385     },
36386
36387     // private
36388     activate : function(){
36389         if(this.disabled){
36390             return false;
36391         }
36392         this.component.focus();
36393         this.fireEvent("activate", this);
36394         return true;
36395     },
36396
36397     // private
36398     deactivate : function(){
36399         this.fireEvent("deactivate", this);
36400     },
36401
36402     // private
36403     disable : function(){
36404         this.component.disable();
36405         Roo.menu.Adapter.superclass.disable.call(this);
36406     },
36407
36408     // private
36409     enable : function(){
36410         this.component.enable();
36411         Roo.menu.Adapter.superclass.enable.call(this);
36412     }
36413 });/*
36414  * Based on:
36415  * Ext JS Library 1.1.1
36416  * Copyright(c) 2006-2007, Ext JS, LLC.
36417  *
36418  * Originally Released Under LGPL - original licence link has changed is not relivant.
36419  *
36420  * Fork - LGPL
36421  * <script type="text/javascript">
36422  */
36423
36424 /**
36425  * @class Roo.menu.TextItem
36426  * @extends Roo.menu.BaseItem
36427  * Adds a static text string to a menu, usually used as either a heading or group separator.
36428  * Note: old style constructor with text is still supported.
36429  * 
36430  * @constructor
36431  * Creates a new TextItem
36432  * @param {Object} cfg Configuration
36433  */
36434 Roo.menu.TextItem = function(cfg){
36435     if (typeof(cfg) == 'string') {
36436         this.text = cfg;
36437     } else {
36438         Roo.apply(this,cfg);
36439     }
36440     
36441     Roo.menu.TextItem.superclass.constructor.call(this);
36442 };
36443
36444 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36445     /**
36446      * @cfg {Boolean} text Text to show on item.
36447      */
36448     text : '',
36449     
36450     /**
36451      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36452      */
36453     hideOnClick : false,
36454     /**
36455      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36456      */
36457     itemCls : "x-menu-text",
36458
36459     // private
36460     onRender : function(){
36461         var s = document.createElement("span");
36462         s.className = this.itemCls;
36463         s.innerHTML = this.text;
36464         this.el = s;
36465         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36466     }
36467 });/*
36468  * Based on:
36469  * Ext JS Library 1.1.1
36470  * Copyright(c) 2006-2007, Ext JS, LLC.
36471  *
36472  * Originally Released Under LGPL - original licence link has changed is not relivant.
36473  *
36474  * Fork - LGPL
36475  * <script type="text/javascript">
36476  */
36477
36478 /**
36479  * @class Roo.menu.Separator
36480  * @extends Roo.menu.BaseItem
36481  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36482  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36483  * @constructor
36484  * @param {Object} config Configuration options
36485  */
36486 Roo.menu.Separator = function(config){
36487     Roo.menu.Separator.superclass.constructor.call(this, config);
36488 };
36489
36490 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36491     /**
36492      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36493      */
36494     itemCls : "x-menu-sep",
36495     /**
36496      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36497      */
36498     hideOnClick : false,
36499
36500     // private
36501     onRender : function(li){
36502         var s = document.createElement("span");
36503         s.className = this.itemCls;
36504         s.innerHTML = "&#160;";
36505         this.el = s;
36506         li.addClass("x-menu-sep-li");
36507         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36508     }
36509 });/*
36510  * Based on:
36511  * Ext JS Library 1.1.1
36512  * Copyright(c) 2006-2007, Ext JS, LLC.
36513  *
36514  * Originally Released Under LGPL - original licence link has changed is not relivant.
36515  *
36516  * Fork - LGPL
36517  * <script type="text/javascript">
36518  */
36519 /**
36520  * @class Roo.menu.Item
36521  * @extends Roo.menu.BaseItem
36522  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36523  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36524  * activation and click handling.
36525  * @constructor
36526  * Creates a new Item
36527  * @param {Object} config Configuration options
36528  */
36529 Roo.menu.Item = function(config){
36530     Roo.menu.Item.superclass.constructor.call(this, config);
36531     if(this.menu){
36532         this.menu = Roo.menu.MenuMgr.get(this.menu);
36533     }
36534 };
36535 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36536     
36537     /**
36538      * @cfg {String} text
36539      * The text to show on the menu item.
36540      */
36541     text: '',
36542      /**
36543      * @cfg {String} HTML to render in menu
36544      * The text to show on the menu item (HTML version).
36545      */
36546     html: '',
36547     /**
36548      * @cfg {String} icon
36549      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36550      */
36551     icon: undefined,
36552     /**
36553      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36554      */
36555     itemCls : "x-menu-item",
36556     /**
36557      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36558      */
36559     canActivate : true,
36560     /**
36561      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36562      */
36563     showDelay: 200,
36564     // doc'd in BaseItem
36565     hideDelay: 200,
36566
36567     // private
36568     ctype: "Roo.menu.Item",
36569     
36570     // private
36571     onRender : function(container, position){
36572         var el = document.createElement("a");
36573         el.hideFocus = true;
36574         el.unselectable = "on";
36575         el.href = this.href || "#";
36576         if(this.hrefTarget){
36577             el.target = this.hrefTarget;
36578         }
36579         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36580         
36581         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36582         
36583         el.innerHTML = String.format(
36584                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36585                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36586         this.el = el;
36587         Roo.menu.Item.superclass.onRender.call(this, container, position);
36588     },
36589
36590     /**
36591      * Sets the text to display in this menu item
36592      * @param {String} text The text to display
36593      * @param {Boolean} isHTML true to indicate text is pure html.
36594      */
36595     setText : function(text, isHTML){
36596         if (isHTML) {
36597             this.html = text;
36598         } else {
36599             this.text = text;
36600             this.html = '';
36601         }
36602         if(this.rendered){
36603             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36604      
36605             this.el.update(String.format(
36606                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36607                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36608             this.parentMenu.autoWidth();
36609         }
36610     },
36611
36612     // private
36613     handleClick : function(e){
36614         if(!this.href){ // if no link defined, stop the event automatically
36615             e.stopEvent();
36616         }
36617         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36618     },
36619
36620     // private
36621     activate : function(autoExpand){
36622         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36623             this.focus();
36624             if(autoExpand){
36625                 this.expandMenu();
36626             }
36627         }
36628         return true;
36629     },
36630
36631     // private
36632     shouldDeactivate : function(e){
36633         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36634             if(this.menu && this.menu.isVisible()){
36635                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36636             }
36637             return true;
36638         }
36639         return false;
36640     },
36641
36642     // private
36643     deactivate : function(){
36644         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36645         this.hideMenu();
36646     },
36647
36648     // private
36649     expandMenu : function(autoActivate){
36650         if(!this.disabled && this.menu){
36651             clearTimeout(this.hideTimer);
36652             delete this.hideTimer;
36653             if(!this.menu.isVisible() && !this.showTimer){
36654                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36655             }else if (this.menu.isVisible() && autoActivate){
36656                 this.menu.tryActivate(0, 1);
36657             }
36658         }
36659     },
36660
36661     // private
36662     deferExpand : function(autoActivate){
36663         delete this.showTimer;
36664         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36665         if(autoActivate){
36666             this.menu.tryActivate(0, 1);
36667         }
36668     },
36669
36670     // private
36671     hideMenu : function(){
36672         clearTimeout(this.showTimer);
36673         delete this.showTimer;
36674         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36675             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36676         }
36677     },
36678
36679     // private
36680     deferHide : function(){
36681         delete this.hideTimer;
36682         this.menu.hide();
36683     }
36684 });/*
36685  * Based on:
36686  * Ext JS Library 1.1.1
36687  * Copyright(c) 2006-2007, Ext JS, LLC.
36688  *
36689  * Originally Released Under LGPL - original licence link has changed is not relivant.
36690  *
36691  * Fork - LGPL
36692  * <script type="text/javascript">
36693  */
36694  
36695 /**
36696  * @class Roo.menu.CheckItem
36697  * @extends Roo.menu.Item
36698  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36699  * @constructor
36700  * Creates a new CheckItem
36701  * @param {Object} config Configuration options
36702  */
36703 Roo.menu.CheckItem = function(config){
36704     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36705     this.addEvents({
36706         /**
36707          * @event beforecheckchange
36708          * Fires before the checked value is set, providing an opportunity to cancel if needed
36709          * @param {Roo.menu.CheckItem} this
36710          * @param {Boolean} checked The new checked value that will be set
36711          */
36712         "beforecheckchange" : true,
36713         /**
36714          * @event checkchange
36715          * Fires after the checked value has been set
36716          * @param {Roo.menu.CheckItem} this
36717          * @param {Boolean} checked The checked value that was set
36718          */
36719         "checkchange" : true
36720     });
36721     if(this.checkHandler){
36722         this.on('checkchange', this.checkHandler, this.scope);
36723     }
36724 };
36725 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36726     /**
36727      * @cfg {String} group
36728      * All check items with the same group name will automatically be grouped into a single-select
36729      * radio button group (defaults to '')
36730      */
36731     /**
36732      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36733      */
36734     itemCls : "x-menu-item x-menu-check-item",
36735     /**
36736      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36737      */
36738     groupClass : "x-menu-group-item",
36739
36740     /**
36741      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36742      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36743      * initialized with checked = true will be rendered as checked.
36744      */
36745     checked: false,
36746
36747     // private
36748     ctype: "Roo.menu.CheckItem",
36749
36750     // private
36751     onRender : function(c){
36752         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36753         if(this.group){
36754             this.el.addClass(this.groupClass);
36755         }
36756         Roo.menu.MenuMgr.registerCheckable(this);
36757         if(this.checked){
36758             this.checked = false;
36759             this.setChecked(true, true);
36760         }
36761     },
36762
36763     // private
36764     destroy : function(){
36765         if(this.rendered){
36766             Roo.menu.MenuMgr.unregisterCheckable(this);
36767         }
36768         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36769     },
36770
36771     /**
36772      * Set the checked state of this item
36773      * @param {Boolean} checked The new checked value
36774      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36775      */
36776     setChecked : function(state, suppressEvent){
36777         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36778             if(this.container){
36779                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36780             }
36781             this.checked = state;
36782             if(suppressEvent !== true){
36783                 this.fireEvent("checkchange", this, state);
36784             }
36785         }
36786     },
36787
36788     // private
36789     handleClick : function(e){
36790        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36791            this.setChecked(!this.checked);
36792        }
36793        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36794     }
36795 });/*
36796  * Based on:
36797  * Ext JS Library 1.1.1
36798  * Copyright(c) 2006-2007, Ext JS, LLC.
36799  *
36800  * Originally Released Under LGPL - original licence link has changed is not relivant.
36801  *
36802  * Fork - LGPL
36803  * <script type="text/javascript">
36804  */
36805  
36806 /**
36807  * @class Roo.menu.DateItem
36808  * @extends Roo.menu.Adapter
36809  * A menu item that wraps the {@link Roo.DatPicker} component.
36810  * @constructor
36811  * Creates a new DateItem
36812  * @param {Object} config Configuration options
36813  */
36814 Roo.menu.DateItem = function(config){
36815     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36816     /** The Roo.DatePicker object @type Roo.DatePicker */
36817     this.picker = this.component;
36818     this.addEvents({select: true});
36819     
36820     this.picker.on("render", function(picker){
36821         picker.getEl().swallowEvent("click");
36822         picker.container.addClass("x-menu-date-item");
36823     });
36824
36825     this.picker.on("select", this.onSelect, this);
36826 };
36827
36828 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36829     // private
36830     onSelect : function(picker, date){
36831         this.fireEvent("select", this, date, picker);
36832         Roo.menu.DateItem.superclass.handleClick.call(this);
36833     }
36834 });/*
36835  * Based on:
36836  * Ext JS Library 1.1.1
36837  * Copyright(c) 2006-2007, Ext JS, LLC.
36838  *
36839  * Originally Released Under LGPL - original licence link has changed is not relivant.
36840  *
36841  * Fork - LGPL
36842  * <script type="text/javascript">
36843  */
36844  
36845 /**
36846  * @class Roo.menu.ColorItem
36847  * @extends Roo.menu.Adapter
36848  * A menu item that wraps the {@link Roo.ColorPalette} component.
36849  * @constructor
36850  * Creates a new ColorItem
36851  * @param {Object} config Configuration options
36852  */
36853 Roo.menu.ColorItem = function(config){
36854     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36855     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36856     this.palette = this.component;
36857     this.relayEvents(this.palette, ["select"]);
36858     if(this.selectHandler){
36859         this.on('select', this.selectHandler, this.scope);
36860     }
36861 };
36862 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36863  * Based on:
36864  * Ext JS Library 1.1.1
36865  * Copyright(c) 2006-2007, Ext JS, LLC.
36866  *
36867  * Originally Released Under LGPL - original licence link has changed is not relivant.
36868  *
36869  * Fork - LGPL
36870  * <script type="text/javascript">
36871  */
36872  
36873
36874 /**
36875  * @class Roo.menu.DateMenu
36876  * @extends Roo.menu.Menu
36877  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36878  * @constructor
36879  * Creates a new DateMenu
36880  * @param {Object} config Configuration options
36881  */
36882 Roo.menu.DateMenu = function(config){
36883     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36884     this.plain = true;
36885     var di = new Roo.menu.DateItem(config);
36886     this.add(di);
36887     /**
36888      * The {@link Roo.DatePicker} instance for this DateMenu
36889      * @type DatePicker
36890      */
36891     this.picker = di.picker;
36892     /**
36893      * @event select
36894      * @param {DatePicker} picker
36895      * @param {Date} date
36896      */
36897     this.relayEvents(di, ["select"]);
36898     this.on('beforeshow', function(){
36899         if(this.picker){
36900             this.picker.hideMonthPicker(false);
36901         }
36902     }, this);
36903 };
36904 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36905     cls:'x-date-menu'
36906 });/*
36907  * Based on:
36908  * Ext JS Library 1.1.1
36909  * Copyright(c) 2006-2007, Ext JS, LLC.
36910  *
36911  * Originally Released Under LGPL - original licence link has changed is not relivant.
36912  *
36913  * Fork - LGPL
36914  * <script type="text/javascript">
36915  */
36916  
36917
36918 /**
36919  * @class Roo.menu.ColorMenu
36920  * @extends Roo.menu.Menu
36921  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36922  * @constructor
36923  * Creates a new ColorMenu
36924  * @param {Object} config Configuration options
36925  */
36926 Roo.menu.ColorMenu = function(config){
36927     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36928     this.plain = true;
36929     var ci = new Roo.menu.ColorItem(config);
36930     this.add(ci);
36931     /**
36932      * The {@link Roo.ColorPalette} instance for this ColorMenu
36933      * @type ColorPalette
36934      */
36935     this.palette = ci.palette;
36936     /**
36937      * @event select
36938      * @param {ColorPalette} palette
36939      * @param {String} color
36940      */
36941     this.relayEvents(ci, ["select"]);
36942 };
36943 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36944  * Based on:
36945  * Ext JS Library 1.1.1
36946  * Copyright(c) 2006-2007, Ext JS, LLC.
36947  *
36948  * Originally Released Under LGPL - original licence link has changed is not relivant.
36949  *
36950  * Fork - LGPL
36951  * <script type="text/javascript">
36952  */
36953  
36954 /**
36955  * @class Roo.form.Field
36956  * @extends Roo.BoxComponent
36957  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36958  * @constructor
36959  * Creates a new Field
36960  * @param {Object} config Configuration options
36961  */
36962 Roo.form.Field = function(config){
36963     Roo.form.Field.superclass.constructor.call(this, config);
36964 };
36965
36966 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36967     /**
36968      * @cfg {String} fieldLabel Label to use when rendering a form.
36969      */
36970        /**
36971      * @cfg {String} qtip Mouse over tip
36972      */
36973      
36974     /**
36975      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36976      */
36977     invalidClass : "x-form-invalid",
36978     /**
36979      * @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")
36980      */
36981     invalidText : "The value in this field is invalid",
36982     /**
36983      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36984      */
36985     focusClass : "x-form-focus",
36986     /**
36987      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36988       automatic validation (defaults to "keyup").
36989      */
36990     validationEvent : "keyup",
36991     /**
36992      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36993      */
36994     validateOnBlur : true,
36995     /**
36996      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36997      */
36998     validationDelay : 250,
36999     /**
37000      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37001      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37002      */
37003     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
37004     /**
37005      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37006      */
37007     fieldClass : "x-form-field",
37008     /**
37009      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37010      *<pre>
37011 Value         Description
37012 -----------   ----------------------------------------------------------------------
37013 qtip          Display a quick tip when the user hovers over the field
37014 title         Display a default browser title attribute popup
37015 under         Add a block div beneath the field containing the error text
37016 side          Add an error icon to the right of the field with a popup on hover
37017 [element id]  Add the error text directly to the innerHTML of the specified element
37018 </pre>
37019      */
37020     msgTarget : 'qtip',
37021     /**
37022      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37023      */
37024     msgFx : 'normal',
37025
37026     /**
37027      * @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.
37028      */
37029     readOnly : false,
37030
37031     /**
37032      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37033      */
37034     disabled : false,
37035
37036     /**
37037      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37038      */
37039     inputType : undefined,
37040     
37041     /**
37042      * @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).
37043          */
37044         tabIndex : undefined,
37045         
37046     // private
37047     isFormField : true,
37048
37049     // private
37050     hasFocus : false,
37051     /**
37052      * @property {Roo.Element} fieldEl
37053      * Element Containing the rendered Field (with label etc.)
37054      */
37055     /**
37056      * @cfg {Mixed} value A value to initialize this field with.
37057      */
37058     value : undefined,
37059
37060     /**
37061      * @cfg {String} name The field's HTML name attribute.
37062      */
37063     /**
37064      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37065      */
37066
37067         // private ??
37068         initComponent : function(){
37069         Roo.form.Field.superclass.initComponent.call(this);
37070         this.addEvents({
37071             /**
37072              * @event focus
37073              * Fires when this field receives input focus.
37074              * @param {Roo.form.Field} this
37075              */
37076             focus : true,
37077             /**
37078              * @event blur
37079              * Fires when this field loses input focus.
37080              * @param {Roo.form.Field} this
37081              */
37082             blur : true,
37083             /**
37084              * @event specialkey
37085              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37086              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37087              * @param {Roo.form.Field} this
37088              * @param {Roo.EventObject} e The event object
37089              */
37090             specialkey : true,
37091             /**
37092              * @event change
37093              * Fires just before the field blurs if the field value has changed.
37094              * @param {Roo.form.Field} this
37095              * @param {Mixed} newValue The new value
37096              * @param {Mixed} oldValue The original value
37097              */
37098             change : true,
37099             /**
37100              * @event invalid
37101              * Fires after the field has been marked as invalid.
37102              * @param {Roo.form.Field} this
37103              * @param {String} msg The validation message
37104              */
37105             invalid : true,
37106             /**
37107              * @event valid
37108              * Fires after the field has been validated with no errors.
37109              * @param {Roo.form.Field} this
37110              */
37111             valid : true,
37112              /**
37113              * @event keyup
37114              * Fires after the key up
37115              * @param {Roo.form.Field} this
37116              * @param {Roo.EventObject}  e The event Object
37117              */
37118             keyup : true
37119         });
37120     },
37121
37122     /**
37123      * Returns the name attribute of the field if available
37124      * @return {String} name The field name
37125      */
37126     getName: function(){
37127          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37128     },
37129
37130     // private
37131     onRender : function(ct, position){
37132         Roo.form.Field.superclass.onRender.call(this, ct, position);
37133         if(!this.el){
37134             var cfg = this.getAutoCreate();
37135             if(!cfg.name){
37136                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37137             }
37138             if (!cfg.name.length) {
37139                 delete cfg.name;
37140             }
37141             if(this.inputType){
37142                 cfg.type = this.inputType;
37143             }
37144             this.el = ct.createChild(cfg, position);
37145         }
37146         var type = this.el.dom.type;
37147         if(type){
37148             if(type == 'password'){
37149                 type = 'text';
37150             }
37151             this.el.addClass('x-form-'+type);
37152         }
37153         if(this.readOnly){
37154             this.el.dom.readOnly = true;
37155         }
37156         if(this.tabIndex !== undefined){
37157             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37158         }
37159
37160         this.el.addClass([this.fieldClass, this.cls]);
37161         this.initValue();
37162     },
37163
37164     /**
37165      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37166      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37167      * @return {Roo.form.Field} this
37168      */
37169     applyTo : function(target){
37170         this.allowDomMove = false;
37171         this.el = Roo.get(target);
37172         this.render(this.el.dom.parentNode);
37173         return this;
37174     },
37175
37176     // private
37177     initValue : function(){
37178         if(this.value !== undefined){
37179             this.setValue(this.value);
37180         }else if(this.el.dom.value.length > 0){
37181             this.setValue(this.el.dom.value);
37182         }
37183     },
37184
37185     /**
37186      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37187      */
37188     isDirty : function() {
37189         if(this.disabled) {
37190             return false;
37191         }
37192         return String(this.getValue()) !== String(this.originalValue);
37193     },
37194
37195     // private
37196     afterRender : function(){
37197         Roo.form.Field.superclass.afterRender.call(this);
37198         this.initEvents();
37199     },
37200
37201     // private
37202     fireKey : function(e){
37203         //Roo.log('field ' + e.getKey());
37204         if(e.isNavKeyPress()){
37205             this.fireEvent("specialkey", this, e);
37206         }
37207     },
37208
37209     /**
37210      * Resets the current field value to the originally loaded value and clears any validation messages
37211      */
37212     reset : function(){
37213         this.setValue(this.resetValue);
37214         this.clearInvalid();
37215     },
37216
37217     // private
37218     initEvents : function(){
37219         // safari killled keypress - so keydown is now used..
37220         this.el.on("keydown" , this.fireKey,  this);
37221         this.el.on("focus", this.onFocus,  this);
37222         this.el.on("blur", this.onBlur,  this);
37223         this.el.relayEvent('keyup', this);
37224
37225         // reference to original value for reset
37226         this.originalValue = this.getValue();
37227         this.resetValue =  this.getValue();
37228     },
37229
37230     // private
37231     onFocus : function(){
37232         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37233             this.el.addClass(this.focusClass);
37234         }
37235         if(!this.hasFocus){
37236             this.hasFocus = true;
37237             this.startValue = this.getValue();
37238             this.fireEvent("focus", this);
37239         }
37240     },
37241
37242     beforeBlur : Roo.emptyFn,
37243
37244     // private
37245     onBlur : function(){
37246         this.beforeBlur();
37247         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37248             this.el.removeClass(this.focusClass);
37249         }
37250         this.hasFocus = false;
37251         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37252             this.validate();
37253         }
37254         var v = this.getValue();
37255         if(String(v) !== String(this.startValue)){
37256             this.fireEvent('change', this, v, this.startValue);
37257         }
37258         this.fireEvent("blur", this);
37259     },
37260
37261     /**
37262      * Returns whether or not the field value is currently valid
37263      * @param {Boolean} preventMark True to disable marking the field invalid
37264      * @return {Boolean} True if the value is valid, else false
37265      */
37266     isValid : function(preventMark){
37267         if(this.disabled){
37268             return true;
37269         }
37270         var restore = this.preventMark;
37271         this.preventMark = preventMark === true;
37272         var v = this.validateValue(this.processValue(this.getRawValue()));
37273         this.preventMark = restore;
37274         return v;
37275     },
37276
37277     /**
37278      * Validates the field value
37279      * @return {Boolean} True if the value is valid, else false
37280      */
37281     validate : function(){
37282         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37283             this.clearInvalid();
37284             return true;
37285         }
37286         return false;
37287     },
37288
37289     processValue : function(value){
37290         return value;
37291     },
37292
37293     // private
37294     // Subclasses should provide the validation implementation by overriding this
37295     validateValue : function(value){
37296         return true;
37297     },
37298
37299     /**
37300      * Mark this field as invalid
37301      * @param {String} msg The validation message
37302      */
37303     markInvalid : function(msg){
37304         if(!this.rendered || this.preventMark){ // not rendered
37305             return;
37306         }
37307         
37308         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37309         
37310         obj.el.addClass(this.invalidClass);
37311         msg = msg || this.invalidText;
37312         switch(this.msgTarget){
37313             case 'qtip':
37314                 obj.el.dom.qtip = msg;
37315                 obj.el.dom.qclass = 'x-form-invalid-tip';
37316                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37317                     Roo.QuickTips.enable();
37318                 }
37319                 break;
37320             case 'title':
37321                 this.el.dom.title = msg;
37322                 break;
37323             case 'under':
37324                 if(!this.errorEl){
37325                     var elp = this.el.findParent('.x-form-element', 5, true);
37326                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37327                     this.errorEl.setWidth(elp.getWidth(true)-20);
37328                 }
37329                 this.errorEl.update(msg);
37330                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37331                 break;
37332             case 'side':
37333                 if(!this.errorIcon){
37334                     var elp = this.el.findParent('.x-form-element', 5, true);
37335                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37336                 }
37337                 this.alignErrorIcon();
37338                 this.errorIcon.dom.qtip = msg;
37339                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37340                 this.errorIcon.show();
37341                 this.on('resize', this.alignErrorIcon, this);
37342                 break;
37343             default:
37344                 var t = Roo.getDom(this.msgTarget);
37345                 t.innerHTML = msg;
37346                 t.style.display = this.msgDisplay;
37347                 break;
37348         }
37349         this.fireEvent('invalid', this, msg);
37350     },
37351
37352     // private
37353     alignErrorIcon : function(){
37354         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37355     },
37356
37357     /**
37358      * Clear any invalid styles/messages for this field
37359      */
37360     clearInvalid : function(){
37361         if(!this.rendered || this.preventMark){ // not rendered
37362             return;
37363         }
37364         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37365         
37366         obj.el.removeClass(this.invalidClass);
37367         switch(this.msgTarget){
37368             case 'qtip':
37369                 obj.el.dom.qtip = '';
37370                 break;
37371             case 'title':
37372                 this.el.dom.title = '';
37373                 break;
37374             case 'under':
37375                 if(this.errorEl){
37376                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37377                 }
37378                 break;
37379             case 'side':
37380                 if(this.errorIcon){
37381                     this.errorIcon.dom.qtip = '';
37382                     this.errorIcon.hide();
37383                     this.un('resize', this.alignErrorIcon, this);
37384                 }
37385                 break;
37386             default:
37387                 var t = Roo.getDom(this.msgTarget);
37388                 t.innerHTML = '';
37389                 t.style.display = 'none';
37390                 break;
37391         }
37392         this.fireEvent('valid', this);
37393     },
37394
37395     /**
37396      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37397      * @return {Mixed} value The field value
37398      */
37399     getRawValue : function(){
37400         var v = this.el.getValue();
37401         
37402         return v;
37403     },
37404
37405     /**
37406      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37407      * @return {Mixed} value The field value
37408      */
37409     getValue : function(){
37410         var v = this.el.getValue();
37411          
37412         return v;
37413     },
37414
37415     /**
37416      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37417      * @param {Mixed} value The value to set
37418      */
37419     setRawValue : function(v){
37420         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37421     },
37422
37423     /**
37424      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37425      * @param {Mixed} value The value to set
37426      */
37427     setValue : function(v){
37428         this.value = v;
37429         if(this.rendered){
37430             this.el.dom.value = (v === null || v === undefined ? '' : v);
37431              this.validate();
37432         }
37433     },
37434
37435     adjustSize : function(w, h){
37436         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37437         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37438         return s;
37439     },
37440
37441     adjustWidth : function(tag, w){
37442         tag = tag.toLowerCase();
37443         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37444             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37445                 if(tag == 'input'){
37446                     return w + 2;
37447                 }
37448                 if(tag == 'textarea'){
37449                     return w-2;
37450                 }
37451             }else if(Roo.isOpera){
37452                 if(tag == 'input'){
37453                     return w + 2;
37454                 }
37455                 if(tag == 'textarea'){
37456                     return w-2;
37457                 }
37458             }
37459         }
37460         return w;
37461     }
37462 });
37463
37464
37465 // anything other than normal should be considered experimental
37466 Roo.form.Field.msgFx = {
37467     normal : {
37468         show: function(msgEl, f){
37469             msgEl.setDisplayed('block');
37470         },
37471
37472         hide : function(msgEl, f){
37473             msgEl.setDisplayed(false).update('');
37474         }
37475     },
37476
37477     slide : {
37478         show: function(msgEl, f){
37479             msgEl.slideIn('t', {stopFx:true});
37480         },
37481
37482         hide : function(msgEl, f){
37483             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37484         }
37485     },
37486
37487     slideRight : {
37488         show: function(msgEl, f){
37489             msgEl.fixDisplay();
37490             msgEl.alignTo(f.el, 'tl-tr');
37491             msgEl.slideIn('l', {stopFx:true});
37492         },
37493
37494         hide : function(msgEl, f){
37495             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37496         }
37497     }
37498 };/*
37499  * Based on:
37500  * Ext JS Library 1.1.1
37501  * Copyright(c) 2006-2007, Ext JS, LLC.
37502  *
37503  * Originally Released Under LGPL - original licence link has changed is not relivant.
37504  *
37505  * Fork - LGPL
37506  * <script type="text/javascript">
37507  */
37508  
37509
37510 /**
37511  * @class Roo.form.TextField
37512  * @extends Roo.form.Field
37513  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37514  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37515  * @constructor
37516  * Creates a new TextField
37517  * @param {Object} config Configuration options
37518  */
37519 Roo.form.TextField = function(config){
37520     Roo.form.TextField.superclass.constructor.call(this, config);
37521     this.addEvents({
37522         /**
37523          * @event autosize
37524          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37525          * according to the default logic, but this event provides a hook for the developer to apply additional
37526          * logic at runtime to resize the field if needed.
37527              * @param {Roo.form.Field} this This text field
37528              * @param {Number} width The new field width
37529              */
37530         autosize : true
37531     });
37532 };
37533
37534 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37535     /**
37536      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37537      */
37538     grow : false,
37539     /**
37540      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37541      */
37542     growMin : 30,
37543     /**
37544      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37545      */
37546     growMax : 800,
37547     /**
37548      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37549      */
37550     vtype : null,
37551     /**
37552      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37553      */
37554     maskRe : null,
37555     /**
37556      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37557      */
37558     disableKeyFilter : false,
37559     /**
37560      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37561      */
37562     allowBlank : true,
37563     /**
37564      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37565      */
37566     minLength : 0,
37567     /**
37568      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37569      */
37570     maxLength : Number.MAX_VALUE,
37571     /**
37572      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37573      */
37574     minLengthText : "The minimum length for this field is {0}",
37575     /**
37576      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37577      */
37578     maxLengthText : "The maximum length for this field is {0}",
37579     /**
37580      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37581      */
37582     selectOnFocus : false,
37583     /**
37584      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37585      */
37586     blankText : "This field is required",
37587     /**
37588      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37589      * If available, this function will be called only after the basic validators all return true, and will be passed the
37590      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37591      */
37592     validator : null,
37593     /**
37594      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37595      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37596      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37597      */
37598     regex : null,
37599     /**
37600      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37601      */
37602     regexText : "",
37603     /**
37604      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37605      */
37606     emptyText : null,
37607    
37608
37609     // private
37610     initEvents : function()
37611     {
37612         if (this.emptyText) {
37613             this.el.attr('placeholder', this.emptyText);
37614         }
37615         
37616         Roo.form.TextField.superclass.initEvents.call(this);
37617         if(this.validationEvent == 'keyup'){
37618             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37619             this.el.on('keyup', this.filterValidation, this);
37620         }
37621         else if(this.validationEvent !== false){
37622             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37623         }
37624         
37625         if(this.selectOnFocus){
37626             this.on("focus", this.preFocus, this);
37627             
37628         }
37629         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37630             this.el.on("keypress", this.filterKeys, this);
37631         }
37632         if(this.grow){
37633             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37634             this.el.on("click", this.autoSize,  this);
37635         }
37636         if(this.el.is('input[type=password]') && Roo.isSafari){
37637             this.el.on('keydown', this.SafariOnKeyDown, this);
37638         }
37639     },
37640
37641     processValue : function(value){
37642         if(this.stripCharsRe){
37643             var newValue = value.replace(this.stripCharsRe, '');
37644             if(newValue !== value){
37645                 this.setRawValue(newValue);
37646                 return newValue;
37647             }
37648         }
37649         return value;
37650     },
37651
37652     filterValidation : function(e){
37653         if(!e.isNavKeyPress()){
37654             this.validationTask.delay(this.validationDelay);
37655         }
37656     },
37657
37658     // private
37659     onKeyUp : function(e){
37660         if(!e.isNavKeyPress()){
37661             this.autoSize();
37662         }
37663     },
37664
37665     /**
37666      * Resets the current field value to the originally-loaded value and clears any validation messages.
37667      *  
37668      */
37669     reset : function(){
37670         Roo.form.TextField.superclass.reset.call(this);
37671        
37672     },
37673
37674     
37675     // private
37676     preFocus : function(){
37677         
37678         if(this.selectOnFocus){
37679             this.el.dom.select();
37680         }
37681     },
37682
37683     
37684     // private
37685     filterKeys : function(e){
37686         var k = e.getKey();
37687         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37688             return;
37689         }
37690         var c = e.getCharCode(), cc = String.fromCharCode(c);
37691         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37692             return;
37693         }
37694         if(!this.maskRe.test(cc)){
37695             e.stopEvent();
37696         }
37697     },
37698
37699     setValue : function(v){
37700         
37701         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37702         
37703         this.autoSize();
37704     },
37705
37706     /**
37707      * Validates a value according to the field's validation rules and marks the field as invalid
37708      * if the validation fails
37709      * @param {Mixed} value The value to validate
37710      * @return {Boolean} True if the value is valid, else false
37711      */
37712     validateValue : function(value){
37713         if(value.length < 1)  { // if it's blank
37714              if(this.allowBlank){
37715                 this.clearInvalid();
37716                 return true;
37717              }else{
37718                 this.markInvalid(this.blankText);
37719                 return false;
37720              }
37721         }
37722         if(value.length < this.minLength){
37723             this.markInvalid(String.format(this.minLengthText, this.minLength));
37724             return false;
37725         }
37726         if(value.length > this.maxLength){
37727             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37728             return false;
37729         }
37730         if(this.vtype){
37731             var vt = Roo.form.VTypes;
37732             if(!vt[this.vtype](value, this)){
37733                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37734                 return false;
37735             }
37736         }
37737         if(typeof this.validator == "function"){
37738             var msg = this.validator(value);
37739             if(msg !== true){
37740                 this.markInvalid(msg);
37741                 return false;
37742             }
37743         }
37744         if(this.regex && !this.regex.test(value)){
37745             this.markInvalid(this.regexText);
37746             return false;
37747         }
37748         return true;
37749     },
37750
37751     /**
37752      * Selects text in this field
37753      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37754      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37755      */
37756     selectText : function(start, end){
37757         var v = this.getRawValue();
37758         if(v.length > 0){
37759             start = start === undefined ? 0 : start;
37760             end = end === undefined ? v.length : end;
37761             var d = this.el.dom;
37762             if(d.setSelectionRange){
37763                 d.setSelectionRange(start, end);
37764             }else if(d.createTextRange){
37765                 var range = d.createTextRange();
37766                 range.moveStart("character", start);
37767                 range.moveEnd("character", v.length-end);
37768                 range.select();
37769             }
37770         }
37771     },
37772
37773     /**
37774      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37775      * This only takes effect if grow = true, and fires the autosize event.
37776      */
37777     autoSize : function(){
37778         if(!this.grow || !this.rendered){
37779             return;
37780         }
37781         if(!this.metrics){
37782             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37783         }
37784         var el = this.el;
37785         var v = el.dom.value;
37786         var d = document.createElement('div');
37787         d.appendChild(document.createTextNode(v));
37788         v = d.innerHTML;
37789         d = null;
37790         v += "&#160;";
37791         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37792         this.el.setWidth(w);
37793         this.fireEvent("autosize", this, w);
37794     },
37795     
37796     // private
37797     SafariOnKeyDown : function(event)
37798     {
37799         // this is a workaround for a password hang bug on chrome/ webkit.
37800         
37801         var isSelectAll = false;
37802         
37803         if(this.el.dom.selectionEnd > 0){
37804             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37805         }
37806         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37807             event.preventDefault();
37808             this.setValue('');
37809             return;
37810         }
37811         
37812         if(isSelectAll){ // backspace and delete key
37813             
37814             event.preventDefault();
37815             // this is very hacky as keydown always get's upper case.
37816             //
37817             var cc = String.fromCharCode(event.getCharCode());
37818             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37819             
37820         }
37821         
37822         
37823     }
37824 });/*
37825  * Based on:
37826  * Ext JS Library 1.1.1
37827  * Copyright(c) 2006-2007, Ext JS, LLC.
37828  *
37829  * Originally Released Under LGPL - original licence link has changed is not relivant.
37830  *
37831  * Fork - LGPL
37832  * <script type="text/javascript">
37833  */
37834  
37835 /**
37836  * @class Roo.form.Hidden
37837  * @extends Roo.form.TextField
37838  * Simple Hidden element used on forms 
37839  * 
37840  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37841  * 
37842  * @constructor
37843  * Creates a new Hidden form element.
37844  * @param {Object} config Configuration options
37845  */
37846
37847
37848
37849 // easy hidden field...
37850 Roo.form.Hidden = function(config){
37851     Roo.form.Hidden.superclass.constructor.call(this, config);
37852 };
37853   
37854 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37855     fieldLabel:      '',
37856     inputType:      'hidden',
37857     width:          50,
37858     allowBlank:     true,
37859     labelSeparator: '',
37860     hidden:         true,
37861     itemCls :       'x-form-item-display-none'
37862
37863
37864 });
37865
37866
37867 /*
37868  * Based on:
37869  * Ext JS Library 1.1.1
37870  * Copyright(c) 2006-2007, Ext JS, LLC.
37871  *
37872  * Originally Released Under LGPL - original licence link has changed is not relivant.
37873  *
37874  * Fork - LGPL
37875  * <script type="text/javascript">
37876  */
37877  
37878 /**
37879  * @class Roo.form.TriggerField
37880  * @extends Roo.form.TextField
37881  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37882  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37883  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37884  * for which you can provide a custom implementation.  For example:
37885  * <pre><code>
37886 var trigger = new Roo.form.TriggerField();
37887 trigger.onTriggerClick = myTriggerFn;
37888 trigger.applyTo('my-field');
37889 </code></pre>
37890  *
37891  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37892  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37893  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37894  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37895  * @constructor
37896  * Create a new TriggerField.
37897  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37898  * to the base TextField)
37899  */
37900 Roo.form.TriggerField = function(config){
37901     this.mimicing = false;
37902     Roo.form.TriggerField.superclass.constructor.call(this, config);
37903 };
37904
37905 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37906     /**
37907      * @cfg {String} triggerClass A CSS class to apply to the trigger
37908      */
37909     /**
37910      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37911      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37912      */
37913     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37914     /**
37915      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37916      */
37917     hideTrigger:false,
37918
37919     /** @cfg {Boolean} grow @hide */
37920     /** @cfg {Number} growMin @hide */
37921     /** @cfg {Number} growMax @hide */
37922
37923     /**
37924      * @hide 
37925      * @method
37926      */
37927     autoSize: Roo.emptyFn,
37928     // private
37929     monitorTab : true,
37930     // private
37931     deferHeight : true,
37932
37933     
37934     actionMode : 'wrap',
37935     // private
37936     onResize : function(w, h){
37937         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37938         if(typeof w == 'number'){
37939             var x = w - this.trigger.getWidth();
37940             this.el.setWidth(this.adjustWidth('input', x));
37941             this.trigger.setStyle('left', x+'px');
37942         }
37943     },
37944
37945     // private
37946     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37947
37948     // private
37949     getResizeEl : function(){
37950         return this.wrap;
37951     },
37952
37953     // private
37954     getPositionEl : function(){
37955         return this.wrap;
37956     },
37957
37958     // private
37959     alignErrorIcon : function(){
37960         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37961     },
37962
37963     // private
37964     onRender : function(ct, position){
37965         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37966         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37967         this.trigger = this.wrap.createChild(this.triggerConfig ||
37968                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37969         if(this.hideTrigger){
37970             this.trigger.setDisplayed(false);
37971         }
37972         this.initTrigger();
37973         if(!this.width){
37974             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37975         }
37976     },
37977
37978     // private
37979     initTrigger : function(){
37980         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37981         this.trigger.addClassOnOver('x-form-trigger-over');
37982         this.trigger.addClassOnClick('x-form-trigger-click');
37983     },
37984
37985     // private
37986     onDestroy : function(){
37987         if(this.trigger){
37988             this.trigger.removeAllListeners();
37989             this.trigger.remove();
37990         }
37991         if(this.wrap){
37992             this.wrap.remove();
37993         }
37994         Roo.form.TriggerField.superclass.onDestroy.call(this);
37995     },
37996
37997     // private
37998     onFocus : function(){
37999         Roo.form.TriggerField.superclass.onFocus.call(this);
38000         if(!this.mimicing){
38001             this.wrap.addClass('x-trigger-wrap-focus');
38002             this.mimicing = true;
38003             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38004             if(this.monitorTab){
38005                 this.el.on("keydown", this.checkTab, this);
38006             }
38007         }
38008     },
38009
38010     // private
38011     checkTab : function(e){
38012         if(e.getKey() == e.TAB){
38013             this.triggerBlur();
38014         }
38015     },
38016
38017     // private
38018     onBlur : function(){
38019         // do nothing
38020     },
38021
38022     // private
38023     mimicBlur : function(e, t){
38024         if(!this.wrap.contains(t) && this.validateBlur()){
38025             this.triggerBlur();
38026         }
38027     },
38028
38029     // private
38030     triggerBlur : function(){
38031         this.mimicing = false;
38032         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38033         if(this.monitorTab){
38034             this.el.un("keydown", this.checkTab, this);
38035         }
38036         this.wrap.removeClass('x-trigger-wrap-focus');
38037         Roo.form.TriggerField.superclass.onBlur.call(this);
38038     },
38039
38040     // private
38041     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38042     validateBlur : function(e, t){
38043         return true;
38044     },
38045
38046     // private
38047     onDisable : function(){
38048         Roo.form.TriggerField.superclass.onDisable.call(this);
38049         if(this.wrap){
38050             this.wrap.addClass('x-item-disabled');
38051         }
38052     },
38053
38054     // private
38055     onEnable : function(){
38056         Roo.form.TriggerField.superclass.onEnable.call(this);
38057         if(this.wrap){
38058             this.wrap.removeClass('x-item-disabled');
38059         }
38060     },
38061
38062     // private
38063     onShow : function(){
38064         var ae = this.getActionEl();
38065         
38066         if(ae){
38067             ae.dom.style.display = '';
38068             ae.dom.style.visibility = 'visible';
38069         }
38070     },
38071
38072     // private
38073     
38074     onHide : function(){
38075         var ae = this.getActionEl();
38076         ae.dom.style.display = 'none';
38077     },
38078
38079     /**
38080      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38081      * by an implementing function.
38082      * @method
38083      * @param {EventObject} e
38084      */
38085     onTriggerClick : Roo.emptyFn
38086 });
38087
38088 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38089 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38090 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38091 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38092     initComponent : function(){
38093         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38094
38095         this.triggerConfig = {
38096             tag:'span', cls:'x-form-twin-triggers', cn:[
38097             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38098             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38099         ]};
38100     },
38101
38102     getTrigger : function(index){
38103         return this.triggers[index];
38104     },
38105
38106     initTrigger : function(){
38107         var ts = this.trigger.select('.x-form-trigger', true);
38108         this.wrap.setStyle('overflow', 'hidden');
38109         var triggerField = this;
38110         ts.each(function(t, all, index){
38111             t.hide = function(){
38112                 var w = triggerField.wrap.getWidth();
38113                 this.dom.style.display = 'none';
38114                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38115             };
38116             t.show = function(){
38117                 var w = triggerField.wrap.getWidth();
38118                 this.dom.style.display = '';
38119                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38120             };
38121             var triggerIndex = 'Trigger'+(index+1);
38122
38123             if(this['hide'+triggerIndex]){
38124                 t.dom.style.display = 'none';
38125             }
38126             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38127             t.addClassOnOver('x-form-trigger-over');
38128             t.addClassOnClick('x-form-trigger-click');
38129         }, this);
38130         this.triggers = ts.elements;
38131     },
38132
38133     onTrigger1Click : Roo.emptyFn,
38134     onTrigger2Click : Roo.emptyFn
38135 });/*
38136  * Based on:
38137  * Ext JS Library 1.1.1
38138  * Copyright(c) 2006-2007, Ext JS, LLC.
38139  *
38140  * Originally Released Under LGPL - original licence link has changed is not relivant.
38141  *
38142  * Fork - LGPL
38143  * <script type="text/javascript">
38144  */
38145  
38146 /**
38147  * @class Roo.form.TextArea
38148  * @extends Roo.form.TextField
38149  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38150  * support for auto-sizing.
38151  * @constructor
38152  * Creates a new TextArea
38153  * @param {Object} config Configuration options
38154  */
38155 Roo.form.TextArea = function(config){
38156     Roo.form.TextArea.superclass.constructor.call(this, config);
38157     // these are provided exchanges for backwards compat
38158     // minHeight/maxHeight were replaced by growMin/growMax to be
38159     // compatible with TextField growing config values
38160     if(this.minHeight !== undefined){
38161         this.growMin = this.minHeight;
38162     }
38163     if(this.maxHeight !== undefined){
38164         this.growMax = this.maxHeight;
38165     }
38166 };
38167
38168 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38169     /**
38170      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38171      */
38172     growMin : 60,
38173     /**
38174      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38175      */
38176     growMax: 1000,
38177     /**
38178      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38179      * in the field (equivalent to setting overflow: hidden, defaults to false)
38180      */
38181     preventScrollbars: false,
38182     /**
38183      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38184      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38185      */
38186
38187     // private
38188     onRender : function(ct, position){
38189         if(!this.el){
38190             this.defaultAutoCreate = {
38191                 tag: "textarea",
38192                 style:"width:300px;height:60px;",
38193                 autocomplete: "off"
38194             };
38195         }
38196         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38197         if(this.grow){
38198             this.textSizeEl = Roo.DomHelper.append(document.body, {
38199                 tag: "pre", cls: "x-form-grow-sizer"
38200             });
38201             if(this.preventScrollbars){
38202                 this.el.setStyle("overflow", "hidden");
38203             }
38204             this.el.setHeight(this.growMin);
38205         }
38206     },
38207
38208     onDestroy : function(){
38209         if(this.textSizeEl){
38210             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38211         }
38212         Roo.form.TextArea.superclass.onDestroy.call(this);
38213     },
38214
38215     // private
38216     onKeyUp : function(e){
38217         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38218             this.autoSize();
38219         }
38220     },
38221
38222     /**
38223      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38224      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38225      */
38226     autoSize : function(){
38227         if(!this.grow || !this.textSizeEl){
38228             return;
38229         }
38230         var el = this.el;
38231         var v = el.dom.value;
38232         var ts = this.textSizeEl;
38233
38234         ts.innerHTML = '';
38235         ts.appendChild(document.createTextNode(v));
38236         v = ts.innerHTML;
38237
38238         Roo.fly(ts).setWidth(this.el.getWidth());
38239         if(v.length < 1){
38240             v = "&#160;&#160;";
38241         }else{
38242             if(Roo.isIE){
38243                 v = v.replace(/\n/g, '<p>&#160;</p>');
38244             }
38245             v += "&#160;\n&#160;";
38246         }
38247         ts.innerHTML = v;
38248         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38249         if(h != this.lastHeight){
38250             this.lastHeight = h;
38251             this.el.setHeight(h);
38252             this.fireEvent("autosize", this, h);
38253         }
38254     }
38255 });/*
38256  * Based on:
38257  * Ext JS Library 1.1.1
38258  * Copyright(c) 2006-2007, Ext JS, LLC.
38259  *
38260  * Originally Released Under LGPL - original licence link has changed is not relivant.
38261  *
38262  * Fork - LGPL
38263  * <script type="text/javascript">
38264  */
38265  
38266
38267 /**
38268  * @class Roo.form.NumberField
38269  * @extends Roo.form.TextField
38270  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38271  * @constructor
38272  * Creates a new NumberField
38273  * @param {Object} config Configuration options
38274  */
38275 Roo.form.NumberField = function(config){
38276     Roo.form.NumberField.superclass.constructor.call(this, config);
38277 };
38278
38279 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38280     /**
38281      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38282      */
38283     fieldClass: "x-form-field x-form-num-field",
38284     /**
38285      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38286      */
38287     allowDecimals : true,
38288     /**
38289      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38290      */
38291     decimalSeparator : ".",
38292     /**
38293      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38294      */
38295     decimalPrecision : 2,
38296     /**
38297      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38298      */
38299     allowNegative : true,
38300     /**
38301      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38302      */
38303     minValue : Number.NEGATIVE_INFINITY,
38304     /**
38305      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38306      */
38307     maxValue : Number.MAX_VALUE,
38308     /**
38309      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38310      */
38311     minText : "The minimum value for this field is {0}",
38312     /**
38313      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38314      */
38315     maxText : "The maximum value for this field is {0}",
38316     /**
38317      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38318      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38319      */
38320     nanText : "{0} is not a valid number",
38321
38322     // private
38323     initEvents : function(){
38324         Roo.form.NumberField.superclass.initEvents.call(this);
38325         var allowed = "0123456789";
38326         if(this.allowDecimals){
38327             allowed += this.decimalSeparator;
38328         }
38329         if(this.allowNegative){
38330             allowed += "-";
38331         }
38332         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38333         var keyPress = function(e){
38334             var k = e.getKey();
38335             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38336                 return;
38337             }
38338             var c = e.getCharCode();
38339             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38340                 e.stopEvent();
38341             }
38342         };
38343         this.el.on("keypress", keyPress, this);
38344     },
38345
38346     // private
38347     validateValue : function(value){
38348         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38349             return false;
38350         }
38351         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38352              return true;
38353         }
38354         var num = this.parseValue(value);
38355         if(isNaN(num)){
38356             this.markInvalid(String.format(this.nanText, value));
38357             return false;
38358         }
38359         if(num < this.minValue){
38360             this.markInvalid(String.format(this.minText, this.minValue));
38361             return false;
38362         }
38363         if(num > this.maxValue){
38364             this.markInvalid(String.format(this.maxText, this.maxValue));
38365             return false;
38366         }
38367         return true;
38368     },
38369
38370     getValue : function(){
38371         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38372     },
38373
38374     // private
38375     parseValue : function(value){
38376         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38377         return isNaN(value) ? '' : value;
38378     },
38379
38380     // private
38381     fixPrecision : function(value){
38382         var nan = isNaN(value);
38383         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38384             return nan ? '' : value;
38385         }
38386         return parseFloat(value).toFixed(this.decimalPrecision);
38387     },
38388
38389     setValue : function(v){
38390         v = this.fixPrecision(v);
38391         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38392     },
38393
38394     // private
38395     decimalPrecisionFcn : function(v){
38396         return Math.floor(v);
38397     },
38398
38399     beforeBlur : function(){
38400         var v = this.parseValue(this.getRawValue());
38401         if(v){
38402             this.setValue(v);
38403         }
38404     }
38405 });/*
38406  * Based on:
38407  * Ext JS Library 1.1.1
38408  * Copyright(c) 2006-2007, Ext JS, LLC.
38409  *
38410  * Originally Released Under LGPL - original licence link has changed is not relivant.
38411  *
38412  * Fork - LGPL
38413  * <script type="text/javascript">
38414  */
38415  
38416 /**
38417  * @class Roo.form.DateField
38418  * @extends Roo.form.TriggerField
38419  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38420 * @constructor
38421 * Create a new DateField
38422 * @param {Object} config
38423  */
38424 Roo.form.DateField = function(config){
38425     Roo.form.DateField.superclass.constructor.call(this, config);
38426     
38427       this.addEvents({
38428          
38429         /**
38430          * @event select
38431          * Fires when a date is selected
38432              * @param {Roo.form.DateField} combo This combo box
38433              * @param {Date} date The date selected
38434              */
38435         'select' : true
38436          
38437     });
38438     
38439     
38440     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38441     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38442     this.ddMatch = null;
38443     if(this.disabledDates){
38444         var dd = this.disabledDates;
38445         var re = "(?:";
38446         for(var i = 0; i < dd.length; i++){
38447             re += dd[i];
38448             if(i != dd.length-1) re += "|";
38449         }
38450         this.ddMatch = new RegExp(re + ")");
38451     }
38452 };
38453
38454 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38455     /**
38456      * @cfg {String} format
38457      * The default date format string which can be overriden for localization support.  The format must be
38458      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38459      */
38460     format : "m/d/y",
38461     /**
38462      * @cfg {String} altFormats
38463      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38464      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38465      */
38466     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38467     /**
38468      * @cfg {Array} disabledDays
38469      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38470      */
38471     disabledDays : null,
38472     /**
38473      * @cfg {String} disabledDaysText
38474      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38475      */
38476     disabledDaysText : "Disabled",
38477     /**
38478      * @cfg {Array} disabledDates
38479      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38480      * expression so they are very powerful. Some examples:
38481      * <ul>
38482      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38483      * <li>["03/08", "09/16"] would disable those days for every year</li>
38484      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38485      * <li>["03/../2006"] would disable every day in March 2006</li>
38486      * <li>["^03"] would disable every day in every March</li>
38487      * </ul>
38488      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38489      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38490      */
38491     disabledDates : null,
38492     /**
38493      * @cfg {String} disabledDatesText
38494      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38495      */
38496     disabledDatesText : "Disabled",
38497     /**
38498      * @cfg {Date/String} minValue
38499      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38500      * valid format (defaults to null).
38501      */
38502     minValue : null,
38503     /**
38504      * @cfg {Date/String} maxValue
38505      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38506      * valid format (defaults to null).
38507      */
38508     maxValue : null,
38509     /**
38510      * @cfg {String} minText
38511      * The error text to display when the date in the cell is before minValue (defaults to
38512      * 'The date in this field must be after {minValue}').
38513      */
38514     minText : "The date in this field must be equal to or after {0}",
38515     /**
38516      * @cfg {String} maxText
38517      * The error text to display when the date in the cell is after maxValue (defaults to
38518      * 'The date in this field must be before {maxValue}').
38519      */
38520     maxText : "The date in this field must be equal to or before {0}",
38521     /**
38522      * @cfg {String} invalidText
38523      * The error text to display when the date in the field is invalid (defaults to
38524      * '{value} is not a valid date - it must be in the format {format}').
38525      */
38526     invalidText : "{0} is not a valid date - it must be in the format {1}",
38527     /**
38528      * @cfg {String} triggerClass
38529      * An additional CSS class used to style the trigger button.  The trigger will always get the
38530      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38531      * which displays a calendar icon).
38532      */
38533     triggerClass : 'x-form-date-trigger',
38534     
38535
38536     /**
38537      * @cfg {Boolean} useIso
38538      * if enabled, then the date field will use a hidden field to store the 
38539      * real value as iso formated date. default (false)
38540      */ 
38541     useIso : false,
38542     /**
38543      * @cfg {String/Object} autoCreate
38544      * A DomHelper element spec, or true for a default element spec (defaults to
38545      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38546      */ 
38547     // private
38548     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38549     
38550     // private
38551     hiddenField: false,
38552     
38553     onRender : function(ct, position)
38554     {
38555         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38556         if (this.useIso) {
38557             //this.el.dom.removeAttribute('name'); 
38558             Roo.log("Changing name?");
38559             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38560             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38561                     'before', true);
38562             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38563             // prevent input submission
38564             this.hiddenName = this.name;
38565         }
38566             
38567             
38568     },
38569     
38570     // private
38571     validateValue : function(value)
38572     {
38573         value = this.formatDate(value);
38574         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38575             Roo.log('super failed');
38576             return false;
38577         }
38578         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38579              return true;
38580         }
38581         var svalue = value;
38582         value = this.parseDate(value);
38583         if(!value){
38584             Roo.log('parse date failed' + svalue);
38585             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38586             return false;
38587         }
38588         var time = value.getTime();
38589         if(this.minValue && time < this.minValue.getTime()){
38590             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38591             return false;
38592         }
38593         if(this.maxValue && time > this.maxValue.getTime()){
38594             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38595             return false;
38596         }
38597         if(this.disabledDays){
38598             var day = value.getDay();
38599             for(var i = 0; i < this.disabledDays.length; i++) {
38600                 if(day === this.disabledDays[i]){
38601                     this.markInvalid(this.disabledDaysText);
38602                     return false;
38603                 }
38604             }
38605         }
38606         var fvalue = this.formatDate(value);
38607         if(this.ddMatch && this.ddMatch.test(fvalue)){
38608             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38609             return false;
38610         }
38611         return true;
38612     },
38613
38614     // private
38615     // Provides logic to override the default TriggerField.validateBlur which just returns true
38616     validateBlur : function(){
38617         return !this.menu || !this.menu.isVisible();
38618     },
38619     
38620     getName: function()
38621     {
38622         // returns hidden if it's set..
38623         if (!this.rendered) {return ''};
38624         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38625         
38626     },
38627
38628     /**
38629      * Returns the current date value of the date field.
38630      * @return {Date} The date value
38631      */
38632     getValue : function(){
38633         
38634         return  this.hiddenField ?
38635                 this.hiddenField.value :
38636                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38637     },
38638
38639     /**
38640      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38641      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38642      * (the default format used is "m/d/y").
38643      * <br />Usage:
38644      * <pre><code>
38645 //All of these calls set the same date value (May 4, 2006)
38646
38647 //Pass a date object:
38648 var dt = new Date('5/4/06');
38649 dateField.setValue(dt);
38650
38651 //Pass a date string (default format):
38652 dateField.setValue('5/4/06');
38653
38654 //Pass a date string (custom format):
38655 dateField.format = 'Y-m-d';
38656 dateField.setValue('2006-5-4');
38657 </code></pre>
38658      * @param {String/Date} date The date or valid date string
38659      */
38660     setValue : function(date){
38661         if (this.hiddenField) {
38662             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38663         }
38664         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38665         // make sure the value field is always stored as a date..
38666         this.value = this.parseDate(date);
38667         
38668         
38669     },
38670
38671     // private
38672     parseDate : function(value){
38673         if(!value || value instanceof Date){
38674             return value;
38675         }
38676         var v = Date.parseDate(value, this.format);
38677          if (!v && this.useIso) {
38678             v = Date.parseDate(value, 'Y-m-d');
38679         }
38680         if(!v && this.altFormats){
38681             if(!this.altFormatsArray){
38682                 this.altFormatsArray = this.altFormats.split("|");
38683             }
38684             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38685                 v = Date.parseDate(value, this.altFormatsArray[i]);
38686             }
38687         }
38688         return v;
38689     },
38690
38691     // private
38692     formatDate : function(date, fmt){
38693         return (!date || !(date instanceof Date)) ?
38694                date : date.dateFormat(fmt || this.format);
38695     },
38696
38697     // private
38698     menuListeners : {
38699         select: function(m, d){
38700             
38701             this.setValue(d);
38702             this.fireEvent('select', this, d);
38703         },
38704         show : function(){ // retain focus styling
38705             this.onFocus();
38706         },
38707         hide : function(){
38708             this.focus.defer(10, this);
38709             var ml = this.menuListeners;
38710             this.menu.un("select", ml.select,  this);
38711             this.menu.un("show", ml.show,  this);
38712             this.menu.un("hide", ml.hide,  this);
38713         }
38714     },
38715
38716     // private
38717     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38718     onTriggerClick : function(){
38719         if(this.disabled){
38720             return;
38721         }
38722         if(this.menu == null){
38723             this.menu = new Roo.menu.DateMenu();
38724         }
38725         Roo.apply(this.menu.picker,  {
38726             showClear: this.allowBlank,
38727             minDate : this.minValue,
38728             maxDate : this.maxValue,
38729             disabledDatesRE : this.ddMatch,
38730             disabledDatesText : this.disabledDatesText,
38731             disabledDays : this.disabledDays,
38732             disabledDaysText : this.disabledDaysText,
38733             format : this.useIso ? 'Y-m-d' : this.format,
38734             minText : String.format(this.minText, this.formatDate(this.minValue)),
38735             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38736         });
38737         this.menu.on(Roo.apply({}, this.menuListeners, {
38738             scope:this
38739         }));
38740         this.menu.picker.setValue(this.getValue() || new Date());
38741         this.menu.show(this.el, "tl-bl?");
38742     },
38743
38744     beforeBlur : function(){
38745         var v = this.parseDate(this.getRawValue());
38746         if(v){
38747             this.setValue(v);
38748         }
38749     },
38750
38751     /*@
38752      * overide
38753      * 
38754      */
38755     isDirty : function() {
38756         if(this.disabled) {
38757             return false;
38758         }
38759         
38760         if(typeof(this.startValue) === 'undefined'){
38761             return false;
38762         }
38763         
38764         return String(this.getValue()) !== String(this.startValue);
38765         
38766     }
38767 });/*
38768  * Based on:
38769  * Ext JS Library 1.1.1
38770  * Copyright(c) 2006-2007, Ext JS, LLC.
38771  *
38772  * Originally Released Under LGPL - original licence link has changed is not relivant.
38773  *
38774  * Fork - LGPL
38775  * <script type="text/javascript">
38776  */
38777  
38778 /**
38779  * @class Roo.form.MonthField
38780  * @extends Roo.form.TriggerField
38781  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38782 * @constructor
38783 * Create a new MonthField
38784 * @param {Object} config
38785  */
38786 Roo.form.MonthField = function(config){
38787     
38788     Roo.form.MonthField.superclass.constructor.call(this, config);
38789     
38790       this.addEvents({
38791          
38792         /**
38793          * @event select
38794          * Fires when a date is selected
38795              * @param {Roo.form.MonthFieeld} combo This combo box
38796              * @param {Date} date The date selected
38797              */
38798         'select' : true
38799          
38800     });
38801     
38802     
38803     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38804     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38805     this.ddMatch = null;
38806     if(this.disabledDates){
38807         var dd = this.disabledDates;
38808         var re = "(?:";
38809         for(var i = 0; i < dd.length; i++){
38810             re += dd[i];
38811             if(i != dd.length-1) re += "|";
38812         }
38813         this.ddMatch = new RegExp(re + ")");
38814     }
38815 };
38816
38817 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38818     /**
38819      * @cfg {String} format
38820      * The default date format string which can be overriden for localization support.  The format must be
38821      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38822      */
38823     format : "M Y",
38824     /**
38825      * @cfg {String} altFormats
38826      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38827      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38828      */
38829     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38830     /**
38831      * @cfg {Array} disabledDays
38832      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38833      */
38834     disabledDays : [0,1,2,3,4,5,6],
38835     /**
38836      * @cfg {String} disabledDaysText
38837      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38838      */
38839     disabledDaysText : "Disabled",
38840     /**
38841      * @cfg {Array} disabledDates
38842      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38843      * expression so they are very powerful. Some examples:
38844      * <ul>
38845      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38846      * <li>["03/08", "09/16"] would disable those days for every year</li>
38847      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38848      * <li>["03/../2006"] would disable every day in March 2006</li>
38849      * <li>["^03"] would disable every day in every March</li>
38850      * </ul>
38851      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38852      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38853      */
38854     disabledDates : null,
38855     /**
38856      * @cfg {String} disabledDatesText
38857      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38858      */
38859     disabledDatesText : "Disabled",
38860     /**
38861      * @cfg {Date/String} minValue
38862      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38863      * valid format (defaults to null).
38864      */
38865     minValue : null,
38866     /**
38867      * @cfg {Date/String} maxValue
38868      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38869      * valid format (defaults to null).
38870      */
38871     maxValue : null,
38872     /**
38873      * @cfg {String} minText
38874      * The error text to display when the date in the cell is before minValue (defaults to
38875      * 'The date in this field must be after {minValue}').
38876      */
38877     minText : "The date in this field must be equal to or after {0}",
38878     /**
38879      * @cfg {String} maxTextf
38880      * The error text to display when the date in the cell is after maxValue (defaults to
38881      * 'The date in this field must be before {maxValue}').
38882      */
38883     maxText : "The date in this field must be equal to or before {0}",
38884     /**
38885      * @cfg {String} invalidText
38886      * The error text to display when the date in the field is invalid (defaults to
38887      * '{value} is not a valid date - it must be in the format {format}').
38888      */
38889     invalidText : "{0} is not a valid date - it must be in the format {1}",
38890     /**
38891      * @cfg {String} triggerClass
38892      * An additional CSS class used to style the trigger button.  The trigger will always get the
38893      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38894      * which displays a calendar icon).
38895      */
38896     triggerClass : 'x-form-date-trigger',
38897     
38898
38899     /**
38900      * @cfg {Boolean} useIso
38901      * if enabled, then the date field will use a hidden field to store the 
38902      * real value as iso formated date. default (true)
38903      */ 
38904     useIso : true,
38905     /**
38906      * @cfg {String/Object} autoCreate
38907      * A DomHelper element spec, or true for a default element spec (defaults to
38908      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38909      */ 
38910     // private
38911     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38912     
38913     // private
38914     hiddenField: false,
38915     
38916     hideMonthPicker : false,
38917     
38918     onRender : function(ct, position)
38919     {
38920         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38921         if (this.useIso) {
38922             this.el.dom.removeAttribute('name'); 
38923             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38924                     'before', true);
38925             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38926             // prevent input submission
38927             this.hiddenName = this.name;
38928         }
38929             
38930             
38931     },
38932     
38933     // private
38934     validateValue : function(value)
38935     {
38936         value = this.formatDate(value);
38937         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38938             return false;
38939         }
38940         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38941              return true;
38942         }
38943         var svalue = value;
38944         value = this.parseDate(value);
38945         if(!value){
38946             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38947             return false;
38948         }
38949         var time = value.getTime();
38950         if(this.minValue && time < this.minValue.getTime()){
38951             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38952             return false;
38953         }
38954         if(this.maxValue && time > this.maxValue.getTime()){
38955             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38956             return false;
38957         }
38958         /*if(this.disabledDays){
38959             var day = value.getDay();
38960             for(var i = 0; i < this.disabledDays.length; i++) {
38961                 if(day === this.disabledDays[i]){
38962                     this.markInvalid(this.disabledDaysText);
38963                     return false;
38964                 }
38965             }
38966         }
38967         */
38968         var fvalue = this.formatDate(value);
38969         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38970             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38971             return false;
38972         }
38973         */
38974         return true;
38975     },
38976
38977     // private
38978     // Provides logic to override the default TriggerField.validateBlur which just returns true
38979     validateBlur : function(){
38980         return !this.menu || !this.menu.isVisible();
38981     },
38982
38983     /**
38984      * Returns the current date value of the date field.
38985      * @return {Date} The date value
38986      */
38987     getValue : function(){
38988         
38989         
38990         
38991         return  this.hiddenField ?
38992                 this.hiddenField.value :
38993                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38994     },
38995
38996     /**
38997      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38998      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38999      * (the default format used is "m/d/y").
39000      * <br />Usage:
39001      * <pre><code>
39002 //All of these calls set the same date value (May 4, 2006)
39003
39004 //Pass a date object:
39005 var dt = new Date('5/4/06');
39006 monthField.setValue(dt);
39007
39008 //Pass a date string (default format):
39009 monthField.setValue('5/4/06');
39010
39011 //Pass a date string (custom format):
39012 monthField.format = 'Y-m-d';
39013 monthField.setValue('2006-5-4');
39014 </code></pre>
39015      * @param {String/Date} date The date or valid date string
39016      */
39017     setValue : function(date){
39018         Roo.log('month setValue' + date);
39019         // can only be first of month..
39020         
39021         var val = this.parseDate(date);
39022         
39023         if (this.hiddenField) {
39024             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39025         }
39026         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39027         this.value = this.parseDate(date);
39028     },
39029
39030     // private
39031     parseDate : function(value){
39032         if(!value || value instanceof Date){
39033             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39034             return value;
39035         }
39036         var v = Date.parseDate(value, this.format);
39037         if (!v && this.useIso) {
39038             v = Date.parseDate(value, 'Y-m-d');
39039         }
39040         if (v) {
39041             // 
39042             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39043         }
39044         
39045         
39046         if(!v && this.altFormats){
39047             if(!this.altFormatsArray){
39048                 this.altFormatsArray = this.altFormats.split("|");
39049             }
39050             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39051                 v = Date.parseDate(value, this.altFormatsArray[i]);
39052             }
39053         }
39054         return v;
39055     },
39056
39057     // private
39058     formatDate : function(date, fmt){
39059         return (!date || !(date instanceof Date)) ?
39060                date : date.dateFormat(fmt || this.format);
39061     },
39062
39063     // private
39064     menuListeners : {
39065         select: function(m, d){
39066             this.setValue(d);
39067             this.fireEvent('select', this, d);
39068         },
39069         show : function(){ // retain focus styling
39070             this.onFocus();
39071         },
39072         hide : function(){
39073             this.focus.defer(10, this);
39074             var ml = this.menuListeners;
39075             this.menu.un("select", ml.select,  this);
39076             this.menu.un("show", ml.show,  this);
39077             this.menu.un("hide", ml.hide,  this);
39078         }
39079     },
39080     // private
39081     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39082     onTriggerClick : function(){
39083         if(this.disabled){
39084             return;
39085         }
39086         if(this.menu == null){
39087             this.menu = new Roo.menu.DateMenu();
39088            
39089         }
39090         
39091         Roo.apply(this.menu.picker,  {
39092             
39093             showClear: this.allowBlank,
39094             minDate : this.minValue,
39095             maxDate : this.maxValue,
39096             disabledDatesRE : this.ddMatch,
39097             disabledDatesText : this.disabledDatesText,
39098             
39099             format : this.useIso ? 'Y-m-d' : this.format,
39100             minText : String.format(this.minText, this.formatDate(this.minValue)),
39101             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39102             
39103         });
39104          this.menu.on(Roo.apply({}, this.menuListeners, {
39105             scope:this
39106         }));
39107        
39108         
39109         var m = this.menu;
39110         var p = m.picker;
39111         
39112         // hide month picker get's called when we called by 'before hide';
39113         
39114         var ignorehide = true;
39115         p.hideMonthPicker  = function(disableAnim){
39116             if (ignorehide) {
39117                 return;
39118             }
39119              if(this.monthPicker){
39120                 Roo.log("hideMonthPicker called");
39121                 if(disableAnim === true){
39122                     this.monthPicker.hide();
39123                 }else{
39124                     this.monthPicker.slideOut('t', {duration:.2});
39125                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39126                     p.fireEvent("select", this, this.value);
39127                     m.hide();
39128                 }
39129             }
39130         }
39131         
39132         Roo.log('picker set value');
39133         Roo.log(this.getValue());
39134         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39135         m.show(this.el, 'tl-bl?');
39136         ignorehide  = false;
39137         // this will trigger hideMonthPicker..
39138         
39139         
39140         // hidden the day picker
39141         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39142         
39143         
39144         
39145       
39146         
39147         p.showMonthPicker.defer(100, p);
39148     
39149         
39150        
39151     },
39152
39153     beforeBlur : function(){
39154         var v = this.parseDate(this.getRawValue());
39155         if(v){
39156             this.setValue(v);
39157         }
39158     }
39159
39160     /** @cfg {Boolean} grow @hide */
39161     /** @cfg {Number} growMin @hide */
39162     /** @cfg {Number} growMax @hide */
39163     /**
39164      * @hide
39165      * @method autoSize
39166      */
39167 });/*
39168  * Based on:
39169  * Ext JS Library 1.1.1
39170  * Copyright(c) 2006-2007, Ext JS, LLC.
39171  *
39172  * Originally Released Under LGPL - original licence link has changed is not relivant.
39173  *
39174  * Fork - LGPL
39175  * <script type="text/javascript">
39176  */
39177  
39178
39179 /**
39180  * @class Roo.form.ComboBox
39181  * @extends Roo.form.TriggerField
39182  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39183  * @constructor
39184  * Create a new ComboBox.
39185  * @param {Object} config Configuration options
39186  */
39187 Roo.form.ComboBox = function(config){
39188     Roo.form.ComboBox.superclass.constructor.call(this, config);
39189     this.addEvents({
39190         /**
39191          * @event expand
39192          * Fires when the dropdown list is expanded
39193              * @param {Roo.form.ComboBox} combo This combo box
39194              */
39195         'expand' : true,
39196         /**
39197          * @event collapse
39198          * Fires when the dropdown list is collapsed
39199              * @param {Roo.form.ComboBox} combo This combo box
39200              */
39201         'collapse' : true,
39202         /**
39203          * @event beforeselect
39204          * Fires before a list item is selected. Return false to cancel the selection.
39205              * @param {Roo.form.ComboBox} combo This combo box
39206              * @param {Roo.data.Record} record The data record returned from the underlying store
39207              * @param {Number} index The index of the selected item in the dropdown list
39208              */
39209         'beforeselect' : true,
39210         /**
39211          * @event select
39212          * Fires when a list item is selected
39213              * @param {Roo.form.ComboBox} combo This combo box
39214              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39215              * @param {Number} index The index of the selected item in the dropdown list
39216              */
39217         'select' : true,
39218         /**
39219          * @event beforequery
39220          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39221          * The event object passed has these properties:
39222              * @param {Roo.form.ComboBox} combo This combo box
39223              * @param {String} query The query
39224              * @param {Boolean} forceAll true to force "all" query
39225              * @param {Boolean} cancel true to cancel the query
39226              * @param {Object} e The query event object
39227              */
39228         'beforequery': true,
39229          /**
39230          * @event add
39231          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39232              * @param {Roo.form.ComboBox} combo This combo box
39233              */
39234         'add' : true,
39235         /**
39236          * @event edit
39237          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39238              * @param {Roo.form.ComboBox} combo This combo box
39239              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39240              */
39241         'edit' : true
39242         
39243         
39244     });
39245     if(this.transform){
39246         this.allowDomMove = false;
39247         var s = Roo.getDom(this.transform);
39248         if(!this.hiddenName){
39249             this.hiddenName = s.name;
39250         }
39251         if(!this.store){
39252             this.mode = 'local';
39253             var d = [], opts = s.options;
39254             for(var i = 0, len = opts.length;i < len; i++){
39255                 var o = opts[i];
39256                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39257                 if(o.selected) {
39258                     this.value = value;
39259                 }
39260                 d.push([value, o.text]);
39261             }
39262             this.store = new Roo.data.SimpleStore({
39263                 'id': 0,
39264                 fields: ['value', 'text'],
39265                 data : d
39266             });
39267             this.valueField = 'value';
39268             this.displayField = 'text';
39269         }
39270         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39271         if(!this.lazyRender){
39272             this.target = true;
39273             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39274             s.parentNode.removeChild(s); // remove it
39275             this.render(this.el.parentNode);
39276         }else{
39277             s.parentNode.removeChild(s); // remove it
39278         }
39279
39280     }
39281     if (this.store) {
39282         this.store = Roo.factory(this.store, Roo.data);
39283     }
39284     
39285     this.selectedIndex = -1;
39286     if(this.mode == 'local'){
39287         if(config.queryDelay === undefined){
39288             this.queryDelay = 10;
39289         }
39290         if(config.minChars === undefined){
39291             this.minChars = 0;
39292         }
39293     }
39294 };
39295
39296 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39297     /**
39298      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39299      */
39300     /**
39301      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39302      * rendering into an Roo.Editor, defaults to false)
39303      */
39304     /**
39305      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39306      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39307      */
39308     /**
39309      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39310      */
39311     /**
39312      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39313      * the dropdown list (defaults to undefined, with no header element)
39314      */
39315
39316      /**
39317      * @cfg {String/Roo.Template} tpl The template to use to render the output
39318      */
39319      
39320     // private
39321     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39322     /**
39323      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39324      */
39325     listWidth: undefined,
39326     /**
39327      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39328      * mode = 'remote' or 'text' if mode = 'local')
39329      */
39330     displayField: undefined,
39331     /**
39332      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39333      * mode = 'remote' or 'value' if mode = 'local'). 
39334      * Note: use of a valueField requires the user make a selection
39335      * in order for a value to be mapped.
39336      */
39337     valueField: undefined,
39338     
39339     
39340     /**
39341      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39342      * field's data value (defaults to the underlying DOM element's name)
39343      */
39344     hiddenName: undefined,
39345     /**
39346      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39347      */
39348     listClass: '',
39349     /**
39350      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39351      */
39352     selectedClass: 'x-combo-selected',
39353     /**
39354      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39355      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39356      * which displays a downward arrow icon).
39357      */
39358     triggerClass : 'x-form-arrow-trigger',
39359     /**
39360      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39361      */
39362     shadow:'sides',
39363     /**
39364      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39365      * anchor positions (defaults to 'tl-bl')
39366      */
39367     listAlign: 'tl-bl?',
39368     /**
39369      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39370      */
39371     maxHeight: 300,
39372     /**
39373      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39374      * query specified by the allQuery config option (defaults to 'query')
39375      */
39376     triggerAction: 'query',
39377     /**
39378      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39379      * (defaults to 4, does not apply if editable = false)
39380      */
39381     minChars : 4,
39382     /**
39383      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39384      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39385      */
39386     typeAhead: false,
39387     /**
39388      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39389      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39390      */
39391     queryDelay: 500,
39392     /**
39393      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39394      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39395      */
39396     pageSize: 0,
39397     /**
39398      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39399      * when editable = true (defaults to false)
39400      */
39401     selectOnFocus:false,
39402     /**
39403      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39404      */
39405     queryParam: 'query',
39406     /**
39407      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39408      * when mode = 'remote' (defaults to 'Loading...')
39409      */
39410     loadingText: 'Loading...',
39411     /**
39412      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39413      */
39414     resizable: false,
39415     /**
39416      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39417      */
39418     handleHeight : 8,
39419     /**
39420      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39421      * traditional select (defaults to true)
39422      */
39423     editable: true,
39424     /**
39425      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39426      */
39427     allQuery: '',
39428     /**
39429      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39430      */
39431     mode: 'remote',
39432     /**
39433      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39434      * listWidth has a higher value)
39435      */
39436     minListWidth : 70,
39437     /**
39438      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39439      * allow the user to set arbitrary text into the field (defaults to false)
39440      */
39441     forceSelection:false,
39442     /**
39443      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39444      * if typeAhead = true (defaults to 250)
39445      */
39446     typeAheadDelay : 250,
39447     /**
39448      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39449      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39450      */
39451     valueNotFoundText : undefined,
39452     /**
39453      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39454      */
39455     blockFocus : false,
39456     
39457     /**
39458      * @cfg {Boolean} disableClear Disable showing of clear button.
39459      */
39460     disableClear : false,
39461     /**
39462      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39463      */
39464     alwaysQuery : false,
39465     
39466     //private
39467     addicon : false,
39468     editicon: false,
39469     
39470     // element that contains real text value.. (when hidden is used..)
39471      
39472     // private
39473     onRender : function(ct, position){
39474         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39475         if(this.hiddenName){
39476             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39477                     'before', true);
39478             this.hiddenField.value =
39479                 this.hiddenValue !== undefined ? this.hiddenValue :
39480                 this.value !== undefined ? this.value : '';
39481
39482             // prevent input submission
39483             this.el.dom.removeAttribute('name');
39484              
39485              
39486         }
39487         if(Roo.isGecko){
39488             this.el.dom.setAttribute('autocomplete', 'off');
39489         }
39490
39491         var cls = 'x-combo-list';
39492
39493         this.list = new Roo.Layer({
39494             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39495         });
39496
39497         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39498         this.list.setWidth(lw);
39499         this.list.swallowEvent('mousewheel');
39500         this.assetHeight = 0;
39501
39502         if(this.title){
39503             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39504             this.assetHeight += this.header.getHeight();
39505         }
39506
39507         this.innerList = this.list.createChild({cls:cls+'-inner'});
39508         this.innerList.on('mouseover', this.onViewOver, this);
39509         this.innerList.on('mousemove', this.onViewMove, this);
39510         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39511         
39512         if(this.allowBlank && !this.pageSize && !this.disableClear){
39513             this.footer = this.list.createChild({cls:cls+'-ft'});
39514             this.pageTb = new Roo.Toolbar(this.footer);
39515            
39516         }
39517         if(this.pageSize){
39518             this.footer = this.list.createChild({cls:cls+'-ft'});
39519             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39520                     {pageSize: this.pageSize});
39521             
39522         }
39523         
39524         if (this.pageTb && this.allowBlank && !this.disableClear) {
39525             var _this = this;
39526             this.pageTb.add(new Roo.Toolbar.Fill(), {
39527                 cls: 'x-btn-icon x-btn-clear',
39528                 text: '&#160;',
39529                 handler: function()
39530                 {
39531                     _this.collapse();
39532                     _this.clearValue();
39533                     _this.onSelect(false, -1);
39534                 }
39535             });
39536         }
39537         if (this.footer) {
39538             this.assetHeight += this.footer.getHeight();
39539         }
39540         
39541
39542         if(!this.tpl){
39543             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39544         }
39545
39546         this.view = new Roo.View(this.innerList, this.tpl, {
39547             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39548         });
39549
39550         this.view.on('click', this.onViewClick, this);
39551
39552         this.store.on('beforeload', this.onBeforeLoad, this);
39553         this.store.on('load', this.onLoad, this);
39554         this.store.on('loadexception', this.onLoadException, this);
39555
39556         if(this.resizable){
39557             this.resizer = new Roo.Resizable(this.list,  {
39558                pinned:true, handles:'se'
39559             });
39560             this.resizer.on('resize', function(r, w, h){
39561                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39562                 this.listWidth = w;
39563                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39564                 this.restrictHeight();
39565             }, this);
39566             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39567         }
39568         if(!this.editable){
39569             this.editable = true;
39570             this.setEditable(false);
39571         }  
39572         
39573         
39574         if (typeof(this.events.add.listeners) != 'undefined') {
39575             
39576             this.addicon = this.wrap.createChild(
39577                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39578        
39579             this.addicon.on('click', function(e) {
39580                 this.fireEvent('add', this);
39581             }, this);
39582         }
39583         if (typeof(this.events.edit.listeners) != 'undefined') {
39584             
39585             this.editicon = this.wrap.createChild(
39586                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39587             if (this.addicon) {
39588                 this.editicon.setStyle('margin-left', '40px');
39589             }
39590             this.editicon.on('click', function(e) {
39591                 
39592                 // we fire even  if inothing is selected..
39593                 this.fireEvent('edit', this, this.lastData );
39594                 
39595             }, this);
39596         }
39597         
39598         
39599         
39600     },
39601
39602     // private
39603     initEvents : function(){
39604         Roo.form.ComboBox.superclass.initEvents.call(this);
39605
39606         this.keyNav = new Roo.KeyNav(this.el, {
39607             "up" : function(e){
39608                 this.inKeyMode = true;
39609                 this.selectPrev();
39610             },
39611
39612             "down" : function(e){
39613                 if(!this.isExpanded()){
39614                     this.onTriggerClick();
39615                 }else{
39616                     this.inKeyMode = true;
39617                     this.selectNext();
39618                 }
39619             },
39620
39621             "enter" : function(e){
39622                 this.onViewClick();
39623                 //return true;
39624             },
39625
39626             "esc" : function(e){
39627                 this.collapse();
39628             },
39629
39630             "tab" : function(e){
39631                 this.onViewClick(false);
39632                 this.fireEvent("specialkey", this, e);
39633                 return true;
39634             },
39635
39636             scope : this,
39637
39638             doRelay : function(foo, bar, hname){
39639                 if(hname == 'down' || this.scope.isExpanded()){
39640                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39641                 }
39642                 return true;
39643             },
39644
39645             forceKeyDown: true
39646         });
39647         this.queryDelay = Math.max(this.queryDelay || 10,
39648                 this.mode == 'local' ? 10 : 250);
39649         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39650         if(this.typeAhead){
39651             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39652         }
39653         if(this.editable !== false){
39654             this.el.on("keyup", this.onKeyUp, this);
39655         }
39656         if(this.forceSelection){
39657             this.on('blur', this.doForce, this);
39658         }
39659     },
39660
39661     onDestroy : function(){
39662         if(this.view){
39663             this.view.setStore(null);
39664             this.view.el.removeAllListeners();
39665             this.view.el.remove();
39666             this.view.purgeListeners();
39667         }
39668         if(this.list){
39669             this.list.destroy();
39670         }
39671         if(this.store){
39672             this.store.un('beforeload', this.onBeforeLoad, this);
39673             this.store.un('load', this.onLoad, this);
39674             this.store.un('loadexception', this.onLoadException, this);
39675         }
39676         Roo.form.ComboBox.superclass.onDestroy.call(this);
39677     },
39678
39679     // private
39680     fireKey : function(e){
39681         if(e.isNavKeyPress() && !this.list.isVisible()){
39682             this.fireEvent("specialkey", this, e);
39683         }
39684     },
39685
39686     // private
39687     onResize: function(w, h){
39688         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39689         
39690         if(typeof w != 'number'){
39691             // we do not handle it!?!?
39692             return;
39693         }
39694         var tw = this.trigger.getWidth();
39695         tw += this.addicon ? this.addicon.getWidth() : 0;
39696         tw += this.editicon ? this.editicon.getWidth() : 0;
39697         var x = w - tw;
39698         this.el.setWidth( this.adjustWidth('input', x));
39699             
39700         this.trigger.setStyle('left', x+'px');
39701         
39702         if(this.list && this.listWidth === undefined){
39703             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39704             this.list.setWidth(lw);
39705             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39706         }
39707         
39708     
39709         
39710     },
39711
39712     /**
39713      * Allow or prevent the user from directly editing the field text.  If false is passed,
39714      * the user will only be able to select from the items defined in the dropdown list.  This method
39715      * is the runtime equivalent of setting the 'editable' config option at config time.
39716      * @param {Boolean} value True to allow the user to directly edit the field text
39717      */
39718     setEditable : function(value){
39719         if(value == this.editable){
39720             return;
39721         }
39722         this.editable = value;
39723         if(!value){
39724             this.el.dom.setAttribute('readOnly', true);
39725             this.el.on('mousedown', this.onTriggerClick,  this);
39726             this.el.addClass('x-combo-noedit');
39727         }else{
39728             this.el.dom.setAttribute('readOnly', false);
39729             this.el.un('mousedown', this.onTriggerClick,  this);
39730             this.el.removeClass('x-combo-noedit');
39731         }
39732     },
39733
39734     // private
39735     onBeforeLoad : function(){
39736         if(!this.hasFocus){
39737             return;
39738         }
39739         this.innerList.update(this.loadingText ?
39740                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39741         this.restrictHeight();
39742         this.selectedIndex = -1;
39743     },
39744
39745     // private
39746     onLoad : function(){
39747         if(!this.hasFocus){
39748             return;
39749         }
39750         if(this.store.getCount() > 0){
39751             this.expand();
39752             this.restrictHeight();
39753             if(this.lastQuery == this.allQuery){
39754                 if(this.editable){
39755                     this.el.dom.select();
39756                 }
39757                 if(!this.selectByValue(this.value, true)){
39758                     this.select(0, true);
39759                 }
39760             }else{
39761                 this.selectNext();
39762                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39763                     this.taTask.delay(this.typeAheadDelay);
39764                 }
39765             }
39766         }else{
39767             this.onEmptyResults();
39768         }
39769         //this.el.focus();
39770     },
39771     // private
39772     onLoadException : function()
39773     {
39774         this.collapse();
39775         Roo.log(this.store.reader.jsonData);
39776         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39777             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39778         }
39779         
39780         
39781     },
39782     // private
39783     onTypeAhead : function(){
39784         if(this.store.getCount() > 0){
39785             var r = this.store.getAt(0);
39786             var newValue = r.data[this.displayField];
39787             var len = newValue.length;
39788             var selStart = this.getRawValue().length;
39789             if(selStart != len){
39790                 this.setRawValue(newValue);
39791                 this.selectText(selStart, newValue.length);
39792             }
39793         }
39794     },
39795
39796     // private
39797     onSelect : function(record, index){
39798         if(this.fireEvent('beforeselect', this, record, index) !== false){
39799             this.setFromData(index > -1 ? record.data : false);
39800             this.collapse();
39801             this.fireEvent('select', this, record, index);
39802         }
39803     },
39804
39805     /**
39806      * Returns the currently selected field value or empty string if no value is set.
39807      * @return {String} value The selected value
39808      */
39809     getValue : function(){
39810         if(this.valueField){
39811             return typeof this.value != 'undefined' ? this.value : '';
39812         }else{
39813             return Roo.form.ComboBox.superclass.getValue.call(this);
39814         }
39815     },
39816
39817     /**
39818      * Clears any text/value currently set in the field
39819      */
39820     clearValue : function(){
39821         if(this.hiddenField){
39822             this.hiddenField.value = '';
39823         }
39824         this.value = '';
39825         this.setRawValue('');
39826         this.lastSelectionText = '';
39827         
39828     },
39829
39830     /**
39831      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39832      * will be displayed in the field.  If the value does not match the data value of an existing item,
39833      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39834      * Otherwise the field will be blank (although the value will still be set).
39835      * @param {String} value The value to match
39836      */
39837     setValue : function(v){
39838         var text = v;
39839         if(this.valueField){
39840             var r = this.findRecord(this.valueField, v);
39841             if(r){
39842                 text = r.data[this.displayField];
39843             }else if(this.valueNotFoundText !== undefined){
39844                 text = this.valueNotFoundText;
39845             }
39846         }
39847         this.lastSelectionText = text;
39848         if(this.hiddenField){
39849             this.hiddenField.value = v;
39850         }
39851         Roo.form.ComboBox.superclass.setValue.call(this, text);
39852         this.value = v;
39853     },
39854     /**
39855      * @property {Object} the last set data for the element
39856      */
39857     
39858     lastData : false,
39859     /**
39860      * Sets the value of the field based on a object which is related to the record format for the store.
39861      * @param {Object} value the value to set as. or false on reset?
39862      */
39863     setFromData : function(o){
39864         var dv = ''; // display value
39865         var vv = ''; // value value..
39866         this.lastData = o;
39867         if (this.displayField) {
39868             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39869         } else {
39870             // this is an error condition!!!
39871             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39872         }
39873         
39874         if(this.valueField){
39875             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39876         }
39877         if(this.hiddenField){
39878             this.hiddenField.value = vv;
39879             
39880             this.lastSelectionText = dv;
39881             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39882             this.value = vv;
39883             return;
39884         }
39885         // no hidden field.. - we store the value in 'value', but still display
39886         // display field!!!!
39887         this.lastSelectionText = dv;
39888         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39889         this.value = vv;
39890         
39891         
39892     },
39893     // private
39894     reset : function(){
39895         // overridden so that last data is reset..
39896         this.setValue(this.resetValue);
39897         this.clearInvalid();
39898         this.lastData = false;
39899         if (this.view) {
39900             this.view.clearSelections();
39901         }
39902     },
39903     // private
39904     findRecord : function(prop, value){
39905         var record;
39906         if(this.store.getCount() > 0){
39907             this.store.each(function(r){
39908                 if(r.data[prop] == value){
39909                     record = r;
39910                     return false;
39911                 }
39912                 return true;
39913             });
39914         }
39915         return record;
39916     },
39917     
39918     getName: function()
39919     {
39920         // returns hidden if it's set..
39921         if (!this.rendered) {return ''};
39922         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39923         
39924     },
39925     // private
39926     onViewMove : function(e, t){
39927         this.inKeyMode = false;
39928     },
39929
39930     // private
39931     onViewOver : function(e, t){
39932         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39933             return;
39934         }
39935         var item = this.view.findItemFromChild(t);
39936         if(item){
39937             var index = this.view.indexOf(item);
39938             this.select(index, false);
39939         }
39940     },
39941
39942     // private
39943     onViewClick : function(doFocus)
39944     {
39945         var index = this.view.getSelectedIndexes()[0];
39946         var r = this.store.getAt(index);
39947         if(r){
39948             this.onSelect(r, index);
39949         }
39950         if(doFocus !== false && !this.blockFocus){
39951             this.el.focus();
39952         }
39953     },
39954
39955     // private
39956     restrictHeight : function(){
39957         this.innerList.dom.style.height = '';
39958         var inner = this.innerList.dom;
39959         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39960         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39961         this.list.beginUpdate();
39962         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39963         this.list.alignTo(this.el, this.listAlign);
39964         this.list.endUpdate();
39965     },
39966
39967     // private
39968     onEmptyResults : function(){
39969         this.collapse();
39970     },
39971
39972     /**
39973      * Returns true if the dropdown list is expanded, else false.
39974      */
39975     isExpanded : function(){
39976         return this.list.isVisible();
39977     },
39978
39979     /**
39980      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39981      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39982      * @param {String} value The data value of the item to select
39983      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39984      * selected item if it is not currently in view (defaults to true)
39985      * @return {Boolean} True if the value matched an item in the list, else false
39986      */
39987     selectByValue : function(v, scrollIntoView){
39988         if(v !== undefined && v !== null){
39989             var r = this.findRecord(this.valueField || this.displayField, v);
39990             if(r){
39991                 this.select(this.store.indexOf(r), scrollIntoView);
39992                 return true;
39993             }
39994         }
39995         return false;
39996     },
39997
39998     /**
39999      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40000      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40001      * @param {Number} index The zero-based index of the list item to select
40002      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40003      * selected item if it is not currently in view (defaults to true)
40004      */
40005     select : function(index, scrollIntoView){
40006         this.selectedIndex = index;
40007         this.view.select(index);
40008         if(scrollIntoView !== false){
40009             var el = this.view.getNode(index);
40010             if(el){
40011                 this.innerList.scrollChildIntoView(el, false);
40012             }
40013         }
40014     },
40015
40016     // private
40017     selectNext : function(){
40018         var ct = this.store.getCount();
40019         if(ct > 0){
40020             if(this.selectedIndex == -1){
40021                 this.select(0);
40022             }else if(this.selectedIndex < ct-1){
40023                 this.select(this.selectedIndex+1);
40024             }
40025         }
40026     },
40027
40028     // private
40029     selectPrev : function(){
40030         var ct = this.store.getCount();
40031         if(ct > 0){
40032             if(this.selectedIndex == -1){
40033                 this.select(0);
40034             }else if(this.selectedIndex != 0){
40035                 this.select(this.selectedIndex-1);
40036             }
40037         }
40038     },
40039
40040     // private
40041     onKeyUp : function(e){
40042         if(this.editable !== false && !e.isSpecialKey()){
40043             this.lastKey = e.getKey();
40044             this.dqTask.delay(this.queryDelay);
40045         }
40046     },
40047
40048     // private
40049     validateBlur : function(){
40050         return !this.list || !this.list.isVisible();   
40051     },
40052
40053     // private
40054     initQuery : function(){
40055         this.doQuery(this.getRawValue());
40056     },
40057
40058     // private
40059     doForce : function(){
40060         if(this.el.dom.value.length > 0){
40061             this.el.dom.value =
40062                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40063              
40064         }
40065     },
40066
40067     /**
40068      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40069      * query allowing the query action to be canceled if needed.
40070      * @param {String} query The SQL query to execute
40071      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40072      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40073      * saved in the current store (defaults to false)
40074      */
40075     doQuery : function(q, forceAll){
40076         if(q === undefined || q === null){
40077             q = '';
40078         }
40079         var qe = {
40080             query: q,
40081             forceAll: forceAll,
40082             combo: this,
40083             cancel:false
40084         };
40085         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40086             return false;
40087         }
40088         q = qe.query;
40089         forceAll = qe.forceAll;
40090         if(forceAll === true || (q.length >= this.minChars)){
40091             if(this.lastQuery != q || this.alwaysQuery){
40092                 this.lastQuery = q;
40093                 if(this.mode == 'local'){
40094                     this.selectedIndex = -1;
40095                     if(forceAll){
40096                         this.store.clearFilter();
40097                     }else{
40098                         this.store.filter(this.displayField, q);
40099                     }
40100                     this.onLoad();
40101                 }else{
40102                     this.store.baseParams[this.queryParam] = q;
40103                     this.store.load({
40104                         params: this.getParams(q)
40105                     });
40106                     this.expand();
40107                 }
40108             }else{
40109                 this.selectedIndex = -1;
40110                 this.onLoad();   
40111             }
40112         }
40113     },
40114
40115     // private
40116     getParams : function(q){
40117         var p = {};
40118         //p[this.queryParam] = q;
40119         if(this.pageSize){
40120             p.start = 0;
40121             p.limit = this.pageSize;
40122         }
40123         return p;
40124     },
40125
40126     /**
40127      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40128      */
40129     collapse : function(){
40130         if(!this.isExpanded()){
40131             return;
40132         }
40133         this.list.hide();
40134         Roo.get(document).un('mousedown', this.collapseIf, this);
40135         Roo.get(document).un('mousewheel', this.collapseIf, this);
40136         if (!this.editable) {
40137             Roo.get(document).un('keydown', this.listKeyPress, this);
40138         }
40139         this.fireEvent('collapse', this);
40140     },
40141
40142     // private
40143     collapseIf : function(e){
40144         if(!e.within(this.wrap) && !e.within(this.list)){
40145             this.collapse();
40146         }
40147     },
40148
40149     /**
40150      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40151      */
40152     expand : function(){
40153         if(this.isExpanded() || !this.hasFocus){
40154             return;
40155         }
40156         this.list.alignTo(this.el, this.listAlign);
40157         this.list.show();
40158         Roo.get(document).on('mousedown', this.collapseIf, this);
40159         Roo.get(document).on('mousewheel', this.collapseIf, this);
40160         if (!this.editable) {
40161             Roo.get(document).on('keydown', this.listKeyPress, this);
40162         }
40163         
40164         this.fireEvent('expand', this);
40165     },
40166
40167     // private
40168     // Implements the default empty TriggerField.onTriggerClick function
40169     onTriggerClick : function(){
40170         if(this.disabled){
40171             return;
40172         }
40173         if(this.isExpanded()){
40174             this.collapse();
40175             if (!this.blockFocus) {
40176                 this.el.focus();
40177             }
40178             
40179         }else {
40180             this.hasFocus = true;
40181             if(this.triggerAction == 'all') {
40182                 this.doQuery(this.allQuery, true);
40183             } else {
40184                 this.doQuery(this.getRawValue());
40185             }
40186             if (!this.blockFocus) {
40187                 this.el.focus();
40188             }
40189         }
40190     },
40191     listKeyPress : function(e)
40192     {
40193         //Roo.log('listkeypress');
40194         // scroll to first matching element based on key pres..
40195         if (e.isSpecialKey()) {
40196             return false;
40197         }
40198         var k = String.fromCharCode(e.getKey()).toUpperCase();
40199         //Roo.log(k);
40200         var match  = false;
40201         var csel = this.view.getSelectedNodes();
40202         var cselitem = false;
40203         if (csel.length) {
40204             var ix = this.view.indexOf(csel[0]);
40205             cselitem  = this.store.getAt(ix);
40206             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40207                 cselitem = false;
40208             }
40209             
40210         }
40211         
40212         this.store.each(function(v) { 
40213             if (cselitem) {
40214                 // start at existing selection.
40215                 if (cselitem.id == v.id) {
40216                     cselitem = false;
40217                 }
40218                 return;
40219             }
40220                 
40221             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40222                 match = this.store.indexOf(v);
40223                 return false;
40224             }
40225         }, this);
40226         
40227         if (match === false) {
40228             return true; // no more action?
40229         }
40230         // scroll to?
40231         this.view.select(match);
40232         var sn = Roo.get(this.view.getSelectedNodes()[0])
40233         sn.scrollIntoView(sn.dom.parentNode, false);
40234     }
40235
40236     /** 
40237     * @cfg {Boolean} grow 
40238     * @hide 
40239     */
40240     /** 
40241     * @cfg {Number} growMin 
40242     * @hide 
40243     */
40244     /** 
40245     * @cfg {Number} growMax 
40246     * @hide 
40247     */
40248     /**
40249      * @hide
40250      * @method autoSize
40251      */
40252 });/*
40253  * Copyright(c) 2010-2012, Roo J Solutions Limited
40254  *
40255  * Licence LGPL
40256  *
40257  */
40258
40259 /**
40260  * @class Roo.form.ComboBoxArray
40261  * @extends Roo.form.TextField
40262  * A facebook style adder... for lists of email / people / countries  etc...
40263  * pick multiple items from a combo box, and shows each one.
40264  *
40265  *  Fred [x]  Brian [x]  [Pick another |v]
40266  *
40267  *
40268  *  For this to work: it needs various extra information
40269  *    - normal combo problay has
40270  *      name, hiddenName
40271  *    + displayField, valueField
40272  *
40273  *    For our purpose...
40274  *
40275  *
40276  *   If we change from 'extends' to wrapping...
40277  *   
40278  *  
40279  *
40280  
40281  
40282  * @constructor
40283  * Create a new ComboBoxArray.
40284  * @param {Object} config Configuration options
40285  */
40286  
40287
40288 Roo.form.ComboBoxArray = function(config)
40289 {
40290     this.addEvents({
40291         /**
40292          * @event remove
40293          * Fires when remove the value from the list
40294              * @param {Roo.form.ComboBoxArray} _self This combo box array
40295              * @param {Roo.form.ComboBoxArray.Item} item removed item
40296              */
40297         'remove' : true
40298         
40299         
40300     });
40301     
40302     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40303     
40304     this.items = new Roo.util.MixedCollection(false);
40305     
40306     // construct the child combo...
40307     
40308     
40309     
40310     
40311    
40312     
40313 }
40314
40315  
40316 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40317
40318     /**
40319      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40320      */
40321     
40322     lastData : false,
40323     
40324     // behavies liek a hiddne field
40325     inputType:      'hidden',
40326     /**
40327      * @cfg {Number} width The width of the box that displays the selected element
40328      */ 
40329     width:          300,
40330
40331     
40332     
40333     /**
40334      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40335      */
40336     name : false,
40337     /**
40338      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40339      */
40340     hiddenName : false,
40341     
40342     
40343     // private the array of items that are displayed..
40344     items  : false,
40345     // private - the hidden field el.
40346     hiddenEl : false,
40347     // private - the filed el..
40348     el : false,
40349     
40350     //validateValue : function() { return true; }, // all values are ok!
40351     //onAddClick: function() { },
40352     
40353     onRender : function(ct, position) 
40354     {
40355         
40356         // create the standard hidden element
40357         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40358         
40359         
40360         // give fake names to child combo;
40361         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40362         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40363         
40364         this.combo = Roo.factory(this.combo, Roo.form);
40365         this.combo.onRender(ct, position);
40366         if (typeof(this.combo.width) != 'undefined') {
40367             this.combo.onResize(this.combo.width,0);
40368         }
40369         
40370         this.combo.initEvents();
40371         
40372         // assigned so form know we need to do this..
40373         this.store          = this.combo.store;
40374         this.valueField     = this.combo.valueField;
40375         this.displayField   = this.combo.displayField ;
40376         
40377         
40378         this.combo.wrap.addClass('x-cbarray-grp');
40379         
40380         var cbwrap = this.combo.wrap.createChild(
40381             {tag: 'div', cls: 'x-cbarray-cb'},
40382             this.combo.el.dom
40383         );
40384         
40385              
40386         this.hiddenEl = this.combo.wrap.createChild({
40387             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40388         });
40389         this.el = this.combo.wrap.createChild({
40390             tag: 'input',  type:'hidden' , name: this.name, value : ''
40391         });
40392          //   this.el.dom.removeAttribute("name");
40393         
40394         
40395         this.outerWrap = this.combo.wrap;
40396         this.wrap = cbwrap;
40397         
40398         this.outerWrap.setWidth(this.width);
40399         this.outerWrap.dom.removeChild(this.el.dom);
40400         
40401         this.wrap.dom.appendChild(this.el.dom);
40402         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40403         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40404         
40405         this.combo.trigger.setStyle('position','relative');
40406         this.combo.trigger.setStyle('left', '0px');
40407         this.combo.trigger.setStyle('top', '2px');
40408         
40409         this.combo.el.setStyle('vertical-align', 'text-bottom');
40410         
40411         //this.trigger.setStyle('vertical-align', 'top');
40412         
40413         // this should use the code from combo really... on('add' ....)
40414         if (this.adder) {
40415             
40416         
40417             this.adder = this.outerWrap.createChild(
40418                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40419             var _t = this;
40420             this.adder.on('click', function(e) {
40421                 _t.fireEvent('adderclick', this, e);
40422             }, _t);
40423         }
40424         //var _t = this;
40425         //this.adder.on('click', this.onAddClick, _t);
40426         
40427         
40428         this.combo.on('select', function(cb, rec, ix) {
40429             this.addItem(rec.data);
40430             
40431             cb.setValue('');
40432             cb.el.dom.value = '';
40433             //cb.lastData = rec.data;
40434             // add to list
40435             
40436         }, this);
40437         
40438         
40439     },
40440     
40441     
40442     getName: function()
40443     {
40444         // returns hidden if it's set..
40445         if (!this.rendered) {return ''};
40446         return  this.hiddenName ? this.hiddenName : this.name;
40447         
40448     },
40449     
40450     
40451     onResize: function(w, h){
40452         
40453         return;
40454         // not sure if this is needed..
40455         //this.combo.onResize(w,h);
40456         
40457         if(typeof w != 'number'){
40458             // we do not handle it!?!?
40459             return;
40460         }
40461         var tw = this.combo.trigger.getWidth();
40462         tw += this.addicon ? this.addicon.getWidth() : 0;
40463         tw += this.editicon ? this.editicon.getWidth() : 0;
40464         var x = w - tw;
40465         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40466             
40467         this.combo.trigger.setStyle('left', '0px');
40468         
40469         if(this.list && this.listWidth === undefined){
40470             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40471             this.list.setWidth(lw);
40472             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40473         }
40474         
40475     
40476         
40477     },
40478     
40479     addItem: function(rec)
40480     {
40481         var valueField = this.combo.valueField;
40482         var displayField = this.combo.displayField;
40483         if (this.items.indexOfKey(rec[valueField]) > -1) {
40484             //console.log("GOT " + rec.data.id);
40485             return;
40486         }
40487         
40488         var x = new Roo.form.ComboBoxArray.Item({
40489             //id : rec[this.idField],
40490             data : rec,
40491             displayField : displayField ,
40492             tipField : displayField ,
40493             cb : this
40494         });
40495         // use the 
40496         this.items.add(rec[valueField],x);
40497         // add it before the element..
40498         this.updateHiddenEl();
40499         x.render(this.outerWrap, this.wrap.dom);
40500         // add the image handler..
40501     },
40502     
40503     updateHiddenEl : function()
40504     {
40505         this.validate();
40506         if (!this.hiddenEl) {
40507             return;
40508         }
40509         var ar = [];
40510         var idField = this.combo.valueField;
40511         
40512         this.items.each(function(f) {
40513             ar.push(f.data[idField]);
40514            
40515         });
40516         this.hiddenEl.dom.value = ar.join(',');
40517         this.validate();
40518     },
40519     
40520     reset : function()
40521     {
40522         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40523         this.items.each(function(f) {
40524            f.remove(); 
40525         });
40526         this.el.dom.value = '';
40527         if (this.hiddenEl) {
40528             this.hiddenEl.dom.value = '';
40529         }
40530         
40531     },
40532     getValue: function()
40533     {
40534         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40535     },
40536     setValue: function(v) // not a valid action - must use addItems..
40537     {
40538          
40539         this.reset();
40540         
40541         
40542         
40543         if (this.store.isLocal && (typeof(v) == 'string')) {
40544             // then we can use the store to find the values..
40545             // comma seperated at present.. this needs to allow JSON based encoding..
40546             this.hiddenEl.value  = v;
40547             var v_ar = [];
40548             Roo.each(v.split(','), function(k) {
40549                 Roo.log("CHECK " + this.valueField + ',' + k);
40550                 var li = this.store.query(this.valueField, k);
40551                 if (!li.length) {
40552                     return;
40553                 }
40554                 var add = {};
40555                 add[this.valueField] = k;
40556                 add[this.displayField] = li.item(0).data[this.displayField];
40557                 
40558                 this.addItem(add);
40559             }, this) 
40560              
40561         }
40562         if (typeof(v) == 'object') {
40563             // then let's assume it's an array of objects..
40564             Roo.each(v, function(l) {
40565                 this.addItem(l);
40566             }, this);
40567              
40568         }
40569         
40570         
40571     },
40572     setFromData: function(v)
40573     {
40574         // this recieves an object, if setValues is called.
40575         this.reset();
40576         this.el.dom.value = v[this.displayField];
40577         this.hiddenEl.dom.value = v[this.valueField];
40578         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40579             return;
40580         }
40581         var kv = v[this.valueField];
40582         var dv = v[this.displayField];
40583         kv = typeof(kv) != 'string' ? '' : kv;
40584         dv = typeof(dv) != 'string' ? '' : dv;
40585         
40586         
40587         var keys = kv.split(',');
40588         var display = dv.split(',');
40589         for (var i = 0 ; i < keys.length; i++) {
40590             
40591             add = {};
40592             add[this.valueField] = keys[i];
40593             add[this.displayField] = display[i];
40594             this.addItem(add);
40595         }
40596       
40597         
40598     },
40599     
40600     /**
40601      * Validates the combox array value
40602      * @return {Boolean} True if the value is valid, else false
40603      */
40604     validate : function(){
40605         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40606             this.clearInvalid();
40607             return true;
40608         }
40609         return false;
40610     },
40611     
40612     validateValue : function(value){
40613         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40614         
40615     },
40616     
40617     /*@
40618      * overide
40619      * 
40620      */
40621     isDirty : function() {
40622         if(this.disabled) {
40623             return false;
40624         }
40625         
40626         try {
40627             var d = Roo.decode(String(this.originalValue));
40628         } catch (e) {
40629             return String(this.getValue()) !== String(this.originalValue);
40630         }
40631         
40632         var originalValue = [];
40633         
40634         for (var i = 0; i < d.length; i++){
40635             originalValue.push(d[i][this.valueField]);
40636         }
40637         
40638         return String(this.getValue()) !== String(originalValue.join(','));
40639         
40640     }
40641     
40642 });
40643
40644
40645
40646 /**
40647  * @class Roo.form.ComboBoxArray.Item
40648  * @extends Roo.BoxComponent
40649  * A selected item in the list
40650  *  Fred [x]  Brian [x]  [Pick another |v]
40651  * 
40652  * @constructor
40653  * Create a new item.
40654  * @param {Object} config Configuration options
40655  */
40656  
40657 Roo.form.ComboBoxArray.Item = function(config) {
40658     config.id = Roo.id();
40659     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40660 }
40661
40662 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40663     data : {},
40664     cb: false,
40665     displayField : false,
40666     tipField : false,
40667     
40668     
40669     defaultAutoCreate : {
40670         tag: 'div',
40671         cls: 'x-cbarray-item',
40672         cn : [ 
40673             { tag: 'div' },
40674             {
40675                 tag: 'img',
40676                 width:16,
40677                 height : 16,
40678                 src : Roo.BLANK_IMAGE_URL ,
40679                 align: 'center'
40680             }
40681         ]
40682         
40683     },
40684     
40685  
40686     onRender : function(ct, position)
40687     {
40688         Roo.form.Field.superclass.onRender.call(this, ct, position);
40689         
40690         if(!this.el){
40691             var cfg = this.getAutoCreate();
40692             this.el = ct.createChild(cfg, position);
40693         }
40694         
40695         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40696         
40697         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40698             this.cb.renderer(this.data) :
40699             String.format('{0}',this.data[this.displayField]);
40700         
40701             
40702         this.el.child('div').dom.setAttribute('qtip',
40703                         String.format('{0}',this.data[this.tipField])
40704         );
40705         
40706         this.el.child('img').on('click', this.remove, this);
40707         
40708     },
40709    
40710     remove : function()
40711     {
40712         this.cb.items.remove(this);
40713         this.el.child('img').un('click', this.remove, this);
40714         this.el.remove();
40715         this.cb.updateHiddenEl();
40716         
40717         this.cb.fireEvent('remove', this.cb, this);
40718     }
40719 });/*
40720  * Based on:
40721  * Ext JS Library 1.1.1
40722  * Copyright(c) 2006-2007, Ext JS, LLC.
40723  *
40724  * Originally Released Under LGPL - original licence link has changed is not relivant.
40725  *
40726  * Fork - LGPL
40727  * <script type="text/javascript">
40728  */
40729 /**
40730  * @class Roo.form.Checkbox
40731  * @extends Roo.form.Field
40732  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40733  * @constructor
40734  * Creates a new Checkbox
40735  * @param {Object} config Configuration options
40736  */
40737 Roo.form.Checkbox = function(config){
40738     Roo.form.Checkbox.superclass.constructor.call(this, config);
40739     this.addEvents({
40740         /**
40741          * @event check
40742          * Fires when the checkbox is checked or unchecked.
40743              * @param {Roo.form.Checkbox} this This checkbox
40744              * @param {Boolean} checked The new checked value
40745              */
40746         check : true
40747     });
40748 };
40749
40750 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40751     /**
40752      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40753      */
40754     focusClass : undefined,
40755     /**
40756      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40757      */
40758     fieldClass: "x-form-field",
40759     /**
40760      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40761      */
40762     checked: false,
40763     /**
40764      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40765      * {tag: "input", type: "checkbox", autocomplete: "off"})
40766      */
40767     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40768     /**
40769      * @cfg {String} boxLabel The text that appears beside the checkbox
40770      */
40771     boxLabel : "",
40772     /**
40773      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40774      */  
40775     inputValue : '1',
40776     /**
40777      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40778      */
40779      valueOff: '0', // value when not checked..
40780
40781     actionMode : 'viewEl', 
40782     //
40783     // private
40784     itemCls : 'x-menu-check-item x-form-item',
40785     groupClass : 'x-menu-group-item',
40786     inputType : 'hidden',
40787     
40788     
40789     inSetChecked: false, // check that we are not calling self...
40790     
40791     inputElement: false, // real input element?
40792     basedOn: false, // ????
40793     
40794     isFormField: true, // not sure where this is needed!!!!
40795
40796     onResize : function(){
40797         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40798         if(!this.boxLabel){
40799             this.el.alignTo(this.wrap, 'c-c');
40800         }
40801     },
40802
40803     initEvents : function(){
40804         Roo.form.Checkbox.superclass.initEvents.call(this);
40805         this.el.on("click", this.onClick,  this);
40806         this.el.on("change", this.onClick,  this);
40807     },
40808
40809
40810     getResizeEl : function(){
40811         return this.wrap;
40812     },
40813
40814     getPositionEl : function(){
40815         return this.wrap;
40816     },
40817
40818     // private
40819     onRender : function(ct, position){
40820         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40821         /*
40822         if(this.inputValue !== undefined){
40823             this.el.dom.value = this.inputValue;
40824         }
40825         */
40826         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40827         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40828         var viewEl = this.wrap.createChild({ 
40829             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40830         this.viewEl = viewEl;   
40831         this.wrap.on('click', this.onClick,  this); 
40832         
40833         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40834         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40835         
40836         
40837         
40838         if(this.boxLabel){
40839             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40840         //    viewEl.on('click', this.onClick,  this); 
40841         }
40842         //if(this.checked){
40843             this.setChecked(this.checked);
40844         //}else{
40845             //this.checked = this.el.dom;
40846         //}
40847
40848     },
40849
40850     // private
40851     initValue : Roo.emptyFn,
40852
40853     /**
40854      * Returns the checked state of the checkbox.
40855      * @return {Boolean} True if checked, else false
40856      */
40857     getValue : function(){
40858         if(this.el){
40859             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40860         }
40861         return this.valueOff;
40862         
40863     },
40864
40865         // private
40866     onClick : function(){ 
40867         this.setChecked(!this.checked);
40868
40869         //if(this.el.dom.checked != this.checked){
40870         //    this.setValue(this.el.dom.checked);
40871        // }
40872     },
40873
40874     /**
40875      * Sets the checked state of the checkbox.
40876      * On is always based on a string comparison between inputValue and the param.
40877      * @param {Boolean/String} value - the value to set 
40878      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40879      */
40880     setValue : function(v,suppressEvent){
40881         
40882         
40883         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40884         //if(this.el && this.el.dom){
40885         //    this.el.dom.checked = this.checked;
40886         //    this.el.dom.defaultChecked = this.checked;
40887         //}
40888         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40889         //this.fireEvent("check", this, this.checked);
40890     },
40891     // private..
40892     setChecked : function(state,suppressEvent)
40893     {
40894         if (this.inSetChecked) {
40895             this.checked = state;
40896             return;
40897         }
40898         
40899     
40900         if(this.wrap){
40901             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40902         }
40903         this.checked = state;
40904         if(suppressEvent !== true){
40905             this.fireEvent('check', this, state);
40906         }
40907         this.inSetChecked = true;
40908         this.el.dom.value = state ? this.inputValue : this.valueOff;
40909         this.inSetChecked = false;
40910         
40911     },
40912     // handle setting of hidden value by some other method!!?!?
40913     setFromHidden: function()
40914     {
40915         if(!this.el){
40916             return;
40917         }
40918         //console.log("SET FROM HIDDEN");
40919         //alert('setFrom hidden');
40920         this.setValue(this.el.dom.value);
40921     },
40922     
40923     onDestroy : function()
40924     {
40925         if(this.viewEl){
40926             Roo.get(this.viewEl).remove();
40927         }
40928          
40929         Roo.form.Checkbox.superclass.onDestroy.call(this);
40930     }
40931
40932 });/*
40933  * Based on:
40934  * Ext JS Library 1.1.1
40935  * Copyright(c) 2006-2007, Ext JS, LLC.
40936  *
40937  * Originally Released Under LGPL - original licence link has changed is not relivant.
40938  *
40939  * Fork - LGPL
40940  * <script type="text/javascript">
40941  */
40942  
40943 /**
40944  * @class Roo.form.Radio
40945  * @extends Roo.form.Checkbox
40946  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40947  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40948  * @constructor
40949  * Creates a new Radio
40950  * @param {Object} config Configuration options
40951  */
40952 Roo.form.Radio = function(){
40953     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40954 };
40955 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40956     inputType: 'radio',
40957
40958     /**
40959      * If this radio is part of a group, it will return the selected value
40960      * @return {String}
40961      */
40962     getGroupValue : function(){
40963         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40964     },
40965     
40966     
40967     onRender : function(ct, position){
40968         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40969         
40970         if(this.inputValue !== undefined){
40971             this.el.dom.value = this.inputValue;
40972         }
40973          
40974         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40975         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40976         //var viewEl = this.wrap.createChild({ 
40977         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40978         //this.viewEl = viewEl;   
40979         //this.wrap.on('click', this.onClick,  this); 
40980         
40981         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40982         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40983         
40984         
40985         
40986         if(this.boxLabel){
40987             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40988         //    viewEl.on('click', this.onClick,  this); 
40989         }
40990          if(this.checked){
40991             this.el.dom.checked =   'checked' ;
40992         }
40993          
40994     } 
40995     
40996     
40997 });//<script type="text/javascript">
40998
40999 /*
41000  * Based  Ext JS Library 1.1.1
41001  * Copyright(c) 2006-2007, Ext JS, LLC.
41002  * LGPL
41003  *
41004  */
41005  
41006 /**
41007  * @class Roo.HtmlEditorCore
41008  * @extends Roo.Component
41009  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41010  *
41011  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41012  */
41013
41014 Roo.HtmlEditorCore = function(config){
41015     
41016     
41017     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41018     this.addEvents({
41019         /**
41020          * @event initialize
41021          * Fires when the editor is fully initialized (including the iframe)
41022          * @param {Roo.HtmlEditorCore} this
41023          */
41024         initialize: true,
41025         /**
41026          * @event activate
41027          * Fires when the editor is first receives the focus. Any insertion must wait
41028          * until after this event.
41029          * @param {Roo.HtmlEditorCore} this
41030          */
41031         activate: true,
41032          /**
41033          * @event beforesync
41034          * Fires before the textarea is updated with content from the editor iframe. Return false
41035          * to cancel the sync.
41036          * @param {Roo.HtmlEditorCore} this
41037          * @param {String} html
41038          */
41039         beforesync: true,
41040          /**
41041          * @event beforepush
41042          * Fires before the iframe editor is updated with content from the textarea. Return false
41043          * to cancel the push.
41044          * @param {Roo.HtmlEditorCore} this
41045          * @param {String} html
41046          */
41047         beforepush: true,
41048          /**
41049          * @event sync
41050          * Fires when the textarea is updated with content from the editor iframe.
41051          * @param {Roo.HtmlEditorCore} this
41052          * @param {String} html
41053          */
41054         sync: true,
41055          /**
41056          * @event push
41057          * Fires when the iframe editor is updated with content from the textarea.
41058          * @param {Roo.HtmlEditorCore} this
41059          * @param {String} html
41060          */
41061         push: true,
41062         
41063         /**
41064          * @event editorevent
41065          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41066          * @param {Roo.HtmlEditorCore} this
41067          */
41068         editorevent: true
41069     });
41070      
41071 };
41072
41073
41074 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41075
41076
41077      /**
41078      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41079      */
41080     
41081     owner : false,
41082     
41083      /**
41084      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41085      *                        Roo.resizable.
41086      */
41087     resizable : false,
41088      /**
41089      * @cfg {Number} height (in pixels)
41090      */   
41091     height: 300,
41092    /**
41093      * @cfg {Number} width (in pixels)
41094      */   
41095     width: 500,
41096     
41097     /**
41098      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41099      * 
41100      */
41101     stylesheets: false,
41102     
41103     // id of frame..
41104     frameId: false,
41105     
41106     // private properties
41107     validationEvent : false,
41108     deferHeight: true,
41109     initialized : false,
41110     activated : false,
41111     sourceEditMode : false,
41112     onFocus : Roo.emptyFn,
41113     iframePad:3,
41114     hideMode:'offsets',
41115     
41116     clearUp: true,
41117     
41118      
41119     
41120
41121     /**
41122      * Protected method that will not generally be called directly. It
41123      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41124      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41125      */
41126     getDocMarkup : function(){
41127         // body styles..
41128         var st = '';
41129         Roo.log(this.stylesheets);
41130         
41131         // inherit styels from page...?? 
41132         if (this.stylesheets === false) {
41133             
41134             Roo.get(document.head).select('style').each(function(node) {
41135                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41136             });
41137             
41138             Roo.get(document.head).select('link').each(function(node) { 
41139                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41140             });
41141             
41142         } else if (!this.stylesheets.length) {
41143                 // simple..
41144                 st = '<style type="text/css">' +
41145                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41146                    '</style>';
41147         } else {
41148             Roo.each(this.stylesheets, function(s) {
41149                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41150             });
41151             
41152         }
41153         
41154         st +=  '<style type="text/css">' +
41155             'IMG { cursor: pointer } ' +
41156         '</style>';
41157
41158         
41159         return '<html><head>' + st  +
41160             //<style type="text/css">' +
41161             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41162             //'</style>' +
41163             ' </head><body class="roo-htmleditor-body"></body></html>';
41164     },
41165
41166     // private
41167     onRender : function(ct, position)
41168     {
41169         var _t = this;
41170         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41171         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41172         
41173         
41174         this.el.dom.style.border = '0 none';
41175         this.el.dom.setAttribute('tabIndex', -1);
41176         this.el.addClass('x-hidden hide');
41177         
41178         
41179         
41180         if(Roo.isIE){ // fix IE 1px bogus margin
41181             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41182         }
41183        
41184         
41185         this.frameId = Roo.id();
41186         
41187          
41188         
41189         var iframe = this.owner.wrap.createChild({
41190             tag: 'iframe',
41191             cls: 'form-control', // bootstrap..
41192             id: this.frameId,
41193             name: this.frameId,
41194             frameBorder : 'no',
41195             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41196         }, this.el
41197         );
41198         
41199         
41200         this.iframe = iframe.dom;
41201
41202          this.assignDocWin();
41203         
41204         this.doc.designMode = 'on';
41205        
41206         this.doc.open();
41207         this.doc.write(this.getDocMarkup());
41208         this.doc.close();
41209
41210         
41211         var task = { // must defer to wait for browser to be ready
41212             run : function(){
41213                 //console.log("run task?" + this.doc.readyState);
41214                 this.assignDocWin();
41215                 if(this.doc.body || this.doc.readyState == 'complete'){
41216                     try {
41217                         this.doc.designMode="on";
41218                     } catch (e) {
41219                         return;
41220                     }
41221                     Roo.TaskMgr.stop(task);
41222                     this.initEditor.defer(10, this);
41223                 }
41224             },
41225             interval : 10,
41226             duration: 10000,
41227             scope: this
41228         };
41229         Roo.TaskMgr.start(task);
41230
41231         
41232          
41233     },
41234
41235     // private
41236     onResize : function(w, h)
41237     {
41238          Roo.log('resize: ' +w + ',' + h );
41239         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41240         if(!this.iframe){
41241             return;
41242         }
41243         if(typeof w == 'number'){
41244             
41245             this.iframe.style.width = w + 'px';
41246         }
41247         if(typeof h == 'number'){
41248             
41249             this.iframe.style.height = h + 'px';
41250             if(this.doc){
41251                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41252             }
41253         }
41254         
41255     },
41256
41257     /**
41258      * Toggles the editor between standard and source edit mode.
41259      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41260      */
41261     toggleSourceEdit : function(sourceEditMode){
41262         
41263         this.sourceEditMode = sourceEditMode === true;
41264         
41265         if(this.sourceEditMode){
41266  
41267             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41268             
41269         }else{
41270             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41271             //this.iframe.className = '';
41272             this.deferFocus();
41273         }
41274         //this.setSize(this.owner.wrap.getSize());
41275         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41276     },
41277
41278     
41279   
41280
41281     /**
41282      * Protected method that will not generally be called directly. If you need/want
41283      * custom HTML cleanup, this is the method you should override.
41284      * @param {String} html The HTML to be cleaned
41285      * return {String} The cleaned HTML
41286      */
41287     cleanHtml : function(html){
41288         html = String(html);
41289         if(html.length > 5){
41290             if(Roo.isSafari){ // strip safari nonsense
41291                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41292             }
41293         }
41294         if(html == '&nbsp;'){
41295             html = '';
41296         }
41297         return html;
41298     },
41299
41300     /**
41301      * HTML Editor -> Textarea
41302      * Protected method that will not generally be called directly. Syncs the contents
41303      * of the editor iframe with the textarea.
41304      */
41305     syncValue : function(){
41306         if(this.initialized){
41307             var bd = (this.doc.body || this.doc.documentElement);
41308             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41309             var html = bd.innerHTML;
41310             if(Roo.isSafari){
41311                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41312                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41313                 if(m && m[1]){
41314                     html = '<div style="'+m[0]+'">' + html + '</div>';
41315                 }
41316             }
41317             html = this.cleanHtml(html);
41318             // fix up the special chars.. normaly like back quotes in word...
41319             // however we do not want to do this with chinese..
41320             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41321                 var cc = b.charCodeAt();
41322                 if (
41323                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41324                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41325                     (cc >= 0xf900 && cc < 0xfb00 )
41326                 ) {
41327                         return b;
41328                 }
41329                 return "&#"+cc+";" 
41330             });
41331             if(this.owner.fireEvent('beforesync', this, html) !== false){
41332                 this.el.dom.value = html;
41333                 this.owner.fireEvent('sync', this, html);
41334             }
41335         }
41336     },
41337
41338     /**
41339      * Protected method that will not generally be called directly. Pushes the value of the textarea
41340      * into the iframe editor.
41341      */
41342     pushValue : function(){
41343         if(this.initialized){
41344             var v = this.el.dom.value.trim();
41345             
41346 //            if(v.length < 1){
41347 //                v = '&#160;';
41348 //            }
41349             
41350             if(this.owner.fireEvent('beforepush', this, v) !== false){
41351                 var d = (this.doc.body || this.doc.documentElement);
41352                 d.innerHTML = v;
41353                 this.cleanUpPaste();
41354                 this.el.dom.value = d.innerHTML;
41355                 this.owner.fireEvent('push', this, v);
41356             }
41357         }
41358     },
41359
41360     // private
41361     deferFocus : function(){
41362         this.focus.defer(10, this);
41363     },
41364
41365     // doc'ed in Field
41366     focus : function(){
41367         if(this.win && !this.sourceEditMode){
41368             this.win.focus();
41369         }else{
41370             this.el.focus();
41371         }
41372     },
41373     
41374     assignDocWin: function()
41375     {
41376         var iframe = this.iframe;
41377         
41378          if(Roo.isIE){
41379             this.doc = iframe.contentWindow.document;
41380             this.win = iframe.contentWindow;
41381         } else {
41382             if (!Roo.get(this.frameId)) {
41383                 return;
41384             }
41385             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41386             this.win = Roo.get(this.frameId).dom.contentWindow;
41387         }
41388     },
41389     
41390     // private
41391     initEditor : function(){
41392         //console.log("INIT EDITOR");
41393         this.assignDocWin();
41394         
41395         
41396         
41397         this.doc.designMode="on";
41398         this.doc.open();
41399         this.doc.write(this.getDocMarkup());
41400         this.doc.close();
41401         
41402         var dbody = (this.doc.body || this.doc.documentElement);
41403         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41404         // this copies styles from the containing element into thsi one..
41405         // not sure why we need all of this..
41406         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41407         
41408         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41409         //ss['background-attachment'] = 'fixed'; // w3c
41410         dbody.bgProperties = 'fixed'; // ie
41411         //Roo.DomHelper.applyStyles(dbody, ss);
41412         Roo.EventManager.on(this.doc, {
41413             //'mousedown': this.onEditorEvent,
41414             'mouseup': this.onEditorEvent,
41415             'dblclick': this.onEditorEvent,
41416             'click': this.onEditorEvent,
41417             'keyup': this.onEditorEvent,
41418             buffer:100,
41419             scope: this
41420         });
41421         if(Roo.isGecko){
41422             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41423         }
41424         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41425             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41426         }
41427         this.initialized = true;
41428
41429         this.owner.fireEvent('initialize', this);
41430         this.pushValue();
41431     },
41432
41433     // private
41434     onDestroy : function(){
41435         
41436         
41437         
41438         if(this.rendered){
41439             
41440             //for (var i =0; i < this.toolbars.length;i++) {
41441             //    // fixme - ask toolbars for heights?
41442             //    this.toolbars[i].onDestroy();
41443            // }
41444             
41445             //this.wrap.dom.innerHTML = '';
41446             //this.wrap.remove();
41447         }
41448     },
41449
41450     // private
41451     onFirstFocus : function(){
41452         
41453         this.assignDocWin();
41454         
41455         
41456         this.activated = true;
41457          
41458     
41459         if(Roo.isGecko){ // prevent silly gecko errors
41460             this.win.focus();
41461             var s = this.win.getSelection();
41462             if(!s.focusNode || s.focusNode.nodeType != 3){
41463                 var r = s.getRangeAt(0);
41464                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41465                 r.collapse(true);
41466                 this.deferFocus();
41467             }
41468             try{
41469                 this.execCmd('useCSS', true);
41470                 this.execCmd('styleWithCSS', false);
41471             }catch(e){}
41472         }
41473         this.owner.fireEvent('activate', this);
41474     },
41475
41476     // private
41477     adjustFont: function(btn){
41478         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41479         //if(Roo.isSafari){ // safari
41480         //    adjust *= 2;
41481        // }
41482         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41483         if(Roo.isSafari){ // safari
41484             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41485             v =  (v < 10) ? 10 : v;
41486             v =  (v > 48) ? 48 : v;
41487             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41488             
41489         }
41490         
41491         
41492         v = Math.max(1, v+adjust);
41493         
41494         this.execCmd('FontSize', v  );
41495     },
41496
41497     onEditorEvent : function(e){
41498         this.owner.fireEvent('editorevent', this, e);
41499       //  this.updateToolbar();
41500         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41501     },
41502
41503     insertTag : function(tg)
41504     {
41505         // could be a bit smarter... -> wrap the current selected tRoo..
41506         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41507             
41508             range = this.createRange(this.getSelection());
41509             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41510             wrappingNode.appendChild(range.extractContents());
41511             range.insertNode(wrappingNode);
41512
41513             return;
41514             
41515             
41516             
41517         }
41518         this.execCmd("formatblock",   tg);
41519         
41520     },
41521     
41522     insertText : function(txt)
41523     {
41524         
41525         
41526         var range = this.createRange();
41527         range.deleteContents();
41528                //alert(Sender.getAttribute('label'));
41529                
41530         range.insertNode(this.doc.createTextNode(txt));
41531     } ,
41532     
41533      
41534
41535     /**
41536      * Executes a Midas editor command on the editor document and performs necessary focus and
41537      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41538      * @param {String} cmd The Midas command
41539      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41540      */
41541     relayCmd : function(cmd, value){
41542         this.win.focus();
41543         this.execCmd(cmd, value);
41544         this.owner.fireEvent('editorevent', this);
41545         //this.updateToolbar();
41546         this.owner.deferFocus();
41547     },
41548
41549     /**
41550      * Executes a Midas editor command directly on the editor document.
41551      * For visual commands, you should use {@link #relayCmd} instead.
41552      * <b>This should only be called after the editor is initialized.</b>
41553      * @param {String} cmd The Midas command
41554      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41555      */
41556     execCmd : function(cmd, value){
41557         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41558         this.syncValue();
41559     },
41560  
41561  
41562    
41563     /**
41564      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41565      * to insert tRoo.
41566      * @param {String} text | dom node.. 
41567      */
41568     insertAtCursor : function(text)
41569     {
41570         
41571         
41572         
41573         if(!this.activated){
41574             return;
41575         }
41576         /*
41577         if(Roo.isIE){
41578             this.win.focus();
41579             var r = this.doc.selection.createRange();
41580             if(r){
41581                 r.collapse(true);
41582                 r.pasteHTML(text);
41583                 this.syncValue();
41584                 this.deferFocus();
41585             
41586             }
41587             return;
41588         }
41589         */
41590         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41591             this.win.focus();
41592             
41593             
41594             // from jquery ui (MIT licenced)
41595             var range, node;
41596             var win = this.win;
41597             
41598             if (win.getSelection && win.getSelection().getRangeAt) {
41599                 range = win.getSelection().getRangeAt(0);
41600                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41601                 range.insertNode(node);
41602             } else if (win.document.selection && win.document.selection.createRange) {
41603                 // no firefox support
41604                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41605                 win.document.selection.createRange().pasteHTML(txt);
41606             } else {
41607                 // no firefox support
41608                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41609                 this.execCmd('InsertHTML', txt);
41610             } 
41611             
41612             this.syncValue();
41613             
41614             this.deferFocus();
41615         }
41616     },
41617  // private
41618     mozKeyPress : function(e){
41619         if(e.ctrlKey){
41620             var c = e.getCharCode(), cmd;
41621           
41622             if(c > 0){
41623                 c = String.fromCharCode(c).toLowerCase();
41624                 switch(c){
41625                     case 'b':
41626                         cmd = 'bold';
41627                         break;
41628                     case 'i':
41629                         cmd = 'italic';
41630                         break;
41631                     
41632                     case 'u':
41633                         cmd = 'underline';
41634                         break;
41635                     
41636                     case 'v':
41637                         this.cleanUpPaste.defer(100, this);
41638                         return;
41639                         
41640                 }
41641                 if(cmd){
41642                     this.win.focus();
41643                     this.execCmd(cmd);
41644                     this.deferFocus();
41645                     e.preventDefault();
41646                 }
41647                 
41648             }
41649         }
41650     },
41651
41652     // private
41653     fixKeys : function(){ // load time branching for fastest keydown performance
41654         if(Roo.isIE){
41655             return function(e){
41656                 var k = e.getKey(), r;
41657                 if(k == e.TAB){
41658                     e.stopEvent();
41659                     r = this.doc.selection.createRange();
41660                     if(r){
41661                         r.collapse(true);
41662                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41663                         this.deferFocus();
41664                     }
41665                     return;
41666                 }
41667                 
41668                 if(k == e.ENTER){
41669                     r = this.doc.selection.createRange();
41670                     if(r){
41671                         var target = r.parentElement();
41672                         if(!target || target.tagName.toLowerCase() != 'li'){
41673                             e.stopEvent();
41674                             r.pasteHTML('<br />');
41675                             r.collapse(false);
41676                             r.select();
41677                         }
41678                     }
41679                 }
41680                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41681                     this.cleanUpPaste.defer(100, this);
41682                     return;
41683                 }
41684                 
41685                 
41686             };
41687         }else if(Roo.isOpera){
41688             return function(e){
41689                 var k = e.getKey();
41690                 if(k == e.TAB){
41691                     e.stopEvent();
41692                     this.win.focus();
41693                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41694                     this.deferFocus();
41695                 }
41696                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41697                     this.cleanUpPaste.defer(100, this);
41698                     return;
41699                 }
41700                 
41701             };
41702         }else if(Roo.isSafari){
41703             return function(e){
41704                 var k = e.getKey();
41705                 
41706                 if(k == e.TAB){
41707                     e.stopEvent();
41708                     this.execCmd('InsertText','\t');
41709                     this.deferFocus();
41710                     return;
41711                 }
41712                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41713                     this.cleanUpPaste.defer(100, this);
41714                     return;
41715                 }
41716                 
41717              };
41718         }
41719     }(),
41720     
41721     getAllAncestors: function()
41722     {
41723         var p = this.getSelectedNode();
41724         var a = [];
41725         if (!p) {
41726             a.push(p); // push blank onto stack..
41727             p = this.getParentElement();
41728         }
41729         
41730         
41731         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41732             a.push(p);
41733             p = p.parentNode;
41734         }
41735         a.push(this.doc.body);
41736         return a;
41737     },
41738     lastSel : false,
41739     lastSelNode : false,
41740     
41741     
41742     getSelection : function() 
41743     {
41744         this.assignDocWin();
41745         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41746     },
41747     
41748     getSelectedNode: function() 
41749     {
41750         // this may only work on Gecko!!!
41751         
41752         // should we cache this!!!!
41753         
41754         
41755         
41756          
41757         var range = this.createRange(this.getSelection()).cloneRange();
41758         
41759         if (Roo.isIE) {
41760             var parent = range.parentElement();
41761             while (true) {
41762                 var testRange = range.duplicate();
41763                 testRange.moveToElementText(parent);
41764                 if (testRange.inRange(range)) {
41765                     break;
41766                 }
41767                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41768                     break;
41769                 }
41770                 parent = parent.parentElement;
41771             }
41772             return parent;
41773         }
41774         
41775         // is ancestor a text element.
41776         var ac =  range.commonAncestorContainer;
41777         if (ac.nodeType == 3) {
41778             ac = ac.parentNode;
41779         }
41780         
41781         var ar = ac.childNodes;
41782          
41783         var nodes = [];
41784         var other_nodes = [];
41785         var has_other_nodes = false;
41786         for (var i=0;i<ar.length;i++) {
41787             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41788                 continue;
41789             }
41790             // fullly contained node.
41791             
41792             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41793                 nodes.push(ar[i]);
41794                 continue;
41795             }
41796             
41797             // probably selected..
41798             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41799                 other_nodes.push(ar[i]);
41800                 continue;
41801             }
41802             // outer..
41803             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41804                 continue;
41805             }
41806             
41807             
41808             has_other_nodes = true;
41809         }
41810         if (!nodes.length && other_nodes.length) {
41811             nodes= other_nodes;
41812         }
41813         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41814             return false;
41815         }
41816         
41817         return nodes[0];
41818     },
41819     createRange: function(sel)
41820     {
41821         // this has strange effects when using with 
41822         // top toolbar - not sure if it's a great idea.
41823         //this.editor.contentWindow.focus();
41824         if (typeof sel != "undefined") {
41825             try {
41826                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41827             } catch(e) {
41828                 return this.doc.createRange();
41829             }
41830         } else {
41831             return this.doc.createRange();
41832         }
41833     },
41834     getParentElement: function()
41835     {
41836         
41837         this.assignDocWin();
41838         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41839         
41840         var range = this.createRange(sel);
41841          
41842         try {
41843             var p = range.commonAncestorContainer;
41844             while (p.nodeType == 3) { // text node
41845                 p = p.parentNode;
41846             }
41847             return p;
41848         } catch (e) {
41849             return null;
41850         }
41851     
41852     },
41853     /***
41854      *
41855      * Range intersection.. the hard stuff...
41856      *  '-1' = before
41857      *  '0' = hits..
41858      *  '1' = after.
41859      *         [ -- selected range --- ]
41860      *   [fail]                        [fail]
41861      *
41862      *    basically..
41863      *      if end is before start or  hits it. fail.
41864      *      if start is after end or hits it fail.
41865      *
41866      *   if either hits (but other is outside. - then it's not 
41867      *   
41868      *    
41869      **/
41870     
41871     
41872     // @see http://www.thismuchiknow.co.uk/?p=64.
41873     rangeIntersectsNode : function(range, node)
41874     {
41875         var nodeRange = node.ownerDocument.createRange();
41876         try {
41877             nodeRange.selectNode(node);
41878         } catch (e) {
41879             nodeRange.selectNodeContents(node);
41880         }
41881     
41882         var rangeStartRange = range.cloneRange();
41883         rangeStartRange.collapse(true);
41884     
41885         var rangeEndRange = range.cloneRange();
41886         rangeEndRange.collapse(false);
41887     
41888         var nodeStartRange = nodeRange.cloneRange();
41889         nodeStartRange.collapse(true);
41890     
41891         var nodeEndRange = nodeRange.cloneRange();
41892         nodeEndRange.collapse(false);
41893     
41894         return rangeStartRange.compareBoundaryPoints(
41895                  Range.START_TO_START, nodeEndRange) == -1 &&
41896                rangeEndRange.compareBoundaryPoints(
41897                  Range.START_TO_START, nodeStartRange) == 1;
41898         
41899          
41900     },
41901     rangeCompareNode : function(range, node)
41902     {
41903         var nodeRange = node.ownerDocument.createRange();
41904         try {
41905             nodeRange.selectNode(node);
41906         } catch (e) {
41907             nodeRange.selectNodeContents(node);
41908         }
41909         
41910         
41911         range.collapse(true);
41912     
41913         nodeRange.collapse(true);
41914      
41915         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41916         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41917          
41918         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41919         
41920         var nodeIsBefore   =  ss == 1;
41921         var nodeIsAfter    = ee == -1;
41922         
41923         if (nodeIsBefore && nodeIsAfter)
41924             return 0; // outer
41925         if (!nodeIsBefore && nodeIsAfter)
41926             return 1; //right trailed.
41927         
41928         if (nodeIsBefore && !nodeIsAfter)
41929             return 2;  // left trailed.
41930         // fully contined.
41931         return 3;
41932     },
41933
41934     // private? - in a new class?
41935     cleanUpPaste :  function()
41936     {
41937         // cleans up the whole document..
41938         Roo.log('cleanuppaste');
41939         
41940         this.cleanUpChildren(this.doc.body);
41941         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41942         if (clean != this.doc.body.innerHTML) {
41943             this.doc.body.innerHTML = clean;
41944         }
41945         
41946     },
41947     
41948     cleanWordChars : function(input) {// change the chars to hex code
41949         var he = Roo.HtmlEditorCore;
41950         
41951         var output = input;
41952         Roo.each(he.swapCodes, function(sw) { 
41953             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41954             
41955             output = output.replace(swapper, sw[1]);
41956         });
41957         
41958         return output;
41959     },
41960     
41961     
41962     cleanUpChildren : function (n)
41963     {
41964         if (!n.childNodes.length) {
41965             return;
41966         }
41967         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41968            this.cleanUpChild(n.childNodes[i]);
41969         }
41970     },
41971     
41972     
41973         
41974     
41975     cleanUpChild : function (node)
41976     {
41977         var ed = this;
41978         //console.log(node);
41979         if (node.nodeName == "#text") {
41980             // clean up silly Windows -- stuff?
41981             return; 
41982         }
41983         if (node.nodeName == "#comment") {
41984             node.parentNode.removeChild(node);
41985             // clean up silly Windows -- stuff?
41986             return; 
41987         }
41988         
41989         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
41990             // remove node.
41991             node.parentNode.removeChild(node);
41992             return;
41993             
41994         }
41995         
41996         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
41997         
41998         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41999         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42000         
42001         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42002         //    remove_keep_children = true;
42003         //}
42004         
42005         if (remove_keep_children) {
42006             this.cleanUpChildren(node);
42007             // inserts everything just before this node...
42008             while (node.childNodes.length) {
42009                 var cn = node.childNodes[0];
42010                 node.removeChild(cn);
42011                 node.parentNode.insertBefore(cn, node);
42012             }
42013             node.parentNode.removeChild(node);
42014             return;
42015         }
42016         
42017         if (!node.attributes || !node.attributes.length) {
42018             this.cleanUpChildren(node);
42019             return;
42020         }
42021         
42022         function cleanAttr(n,v)
42023         {
42024             
42025             if (v.match(/^\./) || v.match(/^\//)) {
42026                 return;
42027             }
42028             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42029                 return;
42030             }
42031             if (v.match(/^#/)) {
42032                 return;
42033             }
42034 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42035             node.removeAttribute(n);
42036             
42037         }
42038         
42039         function cleanStyle(n,v)
42040         {
42041             if (v.match(/expression/)) { //XSS?? should we even bother..
42042                 node.removeAttribute(n);
42043                 return;
42044             }
42045             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
42046             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
42047             
42048             
42049             var parts = v.split(/;/);
42050             var clean = [];
42051             
42052             Roo.each(parts, function(p) {
42053                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42054                 if (!p.length) {
42055                     return true;
42056                 }
42057                 var l = p.split(':').shift().replace(/\s+/g,'');
42058                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42059                 
42060                 if ( cblack.indexOf(l) > -1) {
42061 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42062                     //node.removeAttribute(n);
42063                     return true;
42064                 }
42065                 //Roo.log()
42066                 // only allow 'c whitelisted system attributes'
42067                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42068 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42069                     //node.removeAttribute(n);
42070                     return true;
42071                 }
42072                 
42073                 
42074                  
42075                 
42076                 clean.push(p);
42077                 return true;
42078             });
42079             if (clean.length) { 
42080                 node.setAttribute(n, clean.join(';'));
42081             } else {
42082                 node.removeAttribute(n);
42083             }
42084             
42085         }
42086         
42087         
42088         for (var i = node.attributes.length-1; i > -1 ; i--) {
42089             var a = node.attributes[i];
42090             //console.log(a);
42091             
42092             if (a.name.toLowerCase().substr(0,2)=='on')  {
42093                 node.removeAttribute(a.name);
42094                 continue;
42095             }
42096             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42097                 node.removeAttribute(a.name);
42098                 continue;
42099             }
42100             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42101                 cleanAttr(a.name,a.value); // fixme..
42102                 continue;
42103             }
42104             if (a.name == 'style') {
42105                 cleanStyle(a.name,a.value);
42106                 continue;
42107             }
42108             /// clean up MS crap..
42109             // tecnically this should be a list of valid class'es..
42110             
42111             
42112             if (a.name == 'class') {
42113                 if (a.value.match(/^Mso/)) {
42114                     node.className = '';
42115                 }
42116                 
42117                 if (a.value.match(/body/)) {
42118                     node.className = '';
42119                 }
42120                 continue;
42121             }
42122             
42123             // style cleanup!?
42124             // class cleanup?
42125             
42126         }
42127         
42128         
42129         this.cleanUpChildren(node);
42130         
42131         
42132     },
42133     /**
42134      * Clean up MS wordisms...
42135      */
42136     cleanWord : function(node)
42137     {
42138         var _t = this;
42139         var cleanWordChildren = function()
42140         {
42141             if (!node.childNodes.length) {
42142                 return;
42143             }
42144             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42145                _t.cleanWord(node.childNodes[i]);
42146             }
42147         }
42148         
42149         
42150         if (!node) {
42151             this.cleanWord(this.doc.body);
42152             return;
42153         }
42154         if (node.nodeName == "#text") {
42155             // clean up silly Windows -- stuff?
42156             return; 
42157         }
42158         if (node.nodeName == "#comment") {
42159             node.parentNode.removeChild(node);
42160             // clean up silly Windows -- stuff?
42161             return; 
42162         }
42163         
42164         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42165             node.parentNode.removeChild(node);
42166             return;
42167         }
42168         
42169         // remove - but keep children..
42170         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42171             while (node.childNodes.length) {
42172                 var cn = node.childNodes[0];
42173                 node.removeChild(cn);
42174                 node.parentNode.insertBefore(cn, node);
42175             }
42176             node.parentNode.removeChild(node);
42177             cleanWordChildren();
42178             return;
42179         }
42180         // clean styles
42181         if (node.className.length) {
42182             
42183             var cn = node.className.split(/\W+/);
42184             var cna = [];
42185             Roo.each(cn, function(cls) {
42186                 if (cls.match(/Mso[a-zA-Z]+/)) {
42187                     return;
42188                 }
42189                 cna.push(cls);
42190             });
42191             node.className = cna.length ? cna.join(' ') : '';
42192             if (!cna.length) {
42193                 node.removeAttribute("class");
42194             }
42195         }
42196         
42197         if (node.hasAttribute("lang")) {
42198             node.removeAttribute("lang");
42199         }
42200         
42201         if (node.hasAttribute("style")) {
42202             
42203             var styles = node.getAttribute("style").split(";");
42204             var nstyle = [];
42205             Roo.each(styles, function(s) {
42206                 if (!s.match(/:/)) {
42207                     return;
42208                 }
42209                 var kv = s.split(":");
42210                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42211                     return;
42212                 }
42213                 // what ever is left... we allow.
42214                 nstyle.push(s);
42215             });
42216             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42217             if (!nstyle.length) {
42218                 node.removeAttribute('style');
42219             }
42220         }
42221         
42222         cleanWordChildren();
42223         
42224         
42225     },
42226     domToHTML : function(currentElement, depth, nopadtext) {
42227         
42228             depth = depth || 0;
42229             nopadtext = nopadtext || false;
42230         
42231             if (!currentElement) {
42232                 return this.domToHTML(this.doc.body);
42233             }
42234             
42235             //Roo.log(currentElement);
42236             var j;
42237             var allText = false;
42238             var nodeName = currentElement.nodeName;
42239             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42240             
42241             if  (nodeName == '#text') {
42242                 return currentElement.nodeValue;
42243             }
42244             
42245             
42246             var ret = '';
42247             if (nodeName != 'BODY') {
42248                  
42249                 var i = 0;
42250                 // Prints the node tagName, such as <A>, <IMG>, etc
42251                 if (tagName) {
42252                     var attr = [];
42253                     for(i = 0; i < currentElement.attributes.length;i++) {
42254                         // quoting?
42255                         var aname = currentElement.attributes.item(i).name;
42256                         if (!currentElement.attributes.item(i).value.length) {
42257                             continue;
42258                         }
42259                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42260                     }
42261                     
42262                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42263                 } 
42264                 else {
42265                     
42266                     // eack
42267                 }
42268             } else {
42269                 tagName = false;
42270             }
42271             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42272                 return ret;
42273             }
42274             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42275                 nopadtext = true;
42276             }
42277             
42278             
42279             // Traverse the tree
42280             i = 0;
42281             var currentElementChild = currentElement.childNodes.item(i);
42282             var allText = true;
42283             var innerHTML  = '';
42284             lastnode = '';
42285             while (currentElementChild) {
42286                 // Formatting code (indent the tree so it looks nice on the screen)
42287                 var nopad = nopadtext;
42288                 if (lastnode == 'SPAN') {
42289                     nopad  = true;
42290                 }
42291                 // text
42292                 if  (currentElementChild.nodeName == '#text') {
42293                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42294                     if (!nopad && toadd.length > 80) {
42295                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42296                     }
42297                     innerHTML  += toadd;
42298                     
42299                     i++;
42300                     currentElementChild = currentElement.childNodes.item(i);
42301                     lastNode = '';
42302                     continue;
42303                 }
42304                 allText = false;
42305                 
42306                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42307                     
42308                 // Recursively traverse the tree structure of the child node
42309                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42310                 lastnode = currentElementChild.nodeName;
42311                 i++;
42312                 currentElementChild=currentElement.childNodes.item(i);
42313             }
42314             
42315             ret += innerHTML;
42316             
42317             if (!allText) {
42318                     // The remaining code is mostly for formatting the tree
42319                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42320             }
42321             
42322             
42323             if (tagName) {
42324                 ret+= "</"+tagName+">";
42325             }
42326             return ret;
42327             
42328         }
42329     
42330     // hide stuff that is not compatible
42331     /**
42332      * @event blur
42333      * @hide
42334      */
42335     /**
42336      * @event change
42337      * @hide
42338      */
42339     /**
42340      * @event focus
42341      * @hide
42342      */
42343     /**
42344      * @event specialkey
42345      * @hide
42346      */
42347     /**
42348      * @cfg {String} fieldClass @hide
42349      */
42350     /**
42351      * @cfg {String} focusClass @hide
42352      */
42353     /**
42354      * @cfg {String} autoCreate @hide
42355      */
42356     /**
42357      * @cfg {String} inputType @hide
42358      */
42359     /**
42360      * @cfg {String} invalidClass @hide
42361      */
42362     /**
42363      * @cfg {String} invalidText @hide
42364      */
42365     /**
42366      * @cfg {String} msgFx @hide
42367      */
42368     /**
42369      * @cfg {String} validateOnBlur @hide
42370      */
42371 });
42372
42373 Roo.HtmlEditorCore.white = [
42374         'area', 'br', 'img', 'input', 'hr', 'wbr',
42375         
42376        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42377        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42378        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42379        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42380        'table',   'ul',         'xmp', 
42381        
42382        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42383       'thead',   'tr', 
42384      
42385       'dir', 'menu', 'ol', 'ul', 'dl',
42386        
42387       'embed',  'object'
42388 ];
42389
42390
42391 Roo.HtmlEditorCore.black = [
42392     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42393         'applet', // 
42394         'base',   'basefont', 'bgsound', 'blink',  'body', 
42395         'frame',  'frameset', 'head',    'html',   'ilayer', 
42396         'iframe', 'layer',  'link',     'meta',    'object',   
42397         'script', 'style' ,'title',  'xml' // clean later..
42398 ];
42399 Roo.HtmlEditorCore.clean = [
42400     'script', 'style', 'title', 'xml'
42401 ];
42402 Roo.HtmlEditorCore.remove = [
42403     'font'
42404 ];
42405 // attributes..
42406
42407 Roo.HtmlEditorCore.ablack = [
42408     'on'
42409 ];
42410     
42411 Roo.HtmlEditorCore.aclean = [ 
42412     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42413 ];
42414
42415 // protocols..
42416 Roo.HtmlEditorCore.pwhite= [
42417         'http',  'https',  'mailto'
42418 ];
42419
42420 // white listed style attributes.
42421 Roo.HtmlEditorCore.cwhite= [
42422       //  'text-align', /// default is to allow most things..
42423       
42424          
42425 //        'font-size'//??
42426 ];
42427
42428 // black listed style attributes.
42429 Roo.HtmlEditorCore.cblack= [
42430       //  'font-size' -- this can be set by the project 
42431 ];
42432
42433
42434 Roo.HtmlEditorCore.swapCodes   =[ 
42435     [    8211, "--" ], 
42436     [    8212, "--" ], 
42437     [    8216,  "'" ],  
42438     [    8217, "'" ],  
42439     [    8220, '"' ],  
42440     [    8221, '"' ],  
42441     [    8226, "*" ],  
42442     [    8230, "..." ]
42443 ]; 
42444
42445     //<script type="text/javascript">
42446
42447 /*
42448  * Ext JS Library 1.1.1
42449  * Copyright(c) 2006-2007, Ext JS, LLC.
42450  * Licence LGPL
42451  * 
42452  */
42453  
42454  
42455 Roo.form.HtmlEditor = function(config){
42456     
42457     
42458     
42459     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42460     
42461     if (!this.toolbars) {
42462         this.toolbars = [];
42463     }
42464     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42465     
42466     
42467 };
42468
42469 /**
42470  * @class Roo.form.HtmlEditor
42471  * @extends Roo.form.Field
42472  * Provides a lightweight HTML Editor component.
42473  *
42474  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42475  * 
42476  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42477  * supported by this editor.</b><br/><br/>
42478  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42479  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42480  */
42481 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42482     /**
42483      * @cfg {Boolean} clearUp
42484      */
42485     clearUp : true,
42486       /**
42487      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42488      */
42489     toolbars : false,
42490    
42491      /**
42492      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42493      *                        Roo.resizable.
42494      */
42495     resizable : false,
42496      /**
42497      * @cfg {Number} height (in pixels)
42498      */   
42499     height: 300,
42500    /**
42501      * @cfg {Number} width (in pixels)
42502      */   
42503     width: 500,
42504     
42505     /**
42506      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42507      * 
42508      */
42509     stylesheets: false,
42510     
42511     // id of frame..
42512     frameId: false,
42513     
42514     // private properties
42515     validationEvent : false,
42516     deferHeight: true,
42517     initialized : false,
42518     activated : false,
42519     
42520     onFocus : Roo.emptyFn,
42521     iframePad:3,
42522     hideMode:'offsets',
42523     
42524     defaultAutoCreate : { // modified by initCompnoent..
42525         tag: "textarea",
42526         style:"width:500px;height:300px;",
42527         autocomplete: "off"
42528     },
42529
42530     // private
42531     initComponent : function(){
42532         this.addEvents({
42533             /**
42534              * @event initialize
42535              * Fires when the editor is fully initialized (including the iframe)
42536              * @param {HtmlEditor} this
42537              */
42538             initialize: true,
42539             /**
42540              * @event activate
42541              * Fires when the editor is first receives the focus. Any insertion must wait
42542              * until after this event.
42543              * @param {HtmlEditor} this
42544              */
42545             activate: true,
42546              /**
42547              * @event beforesync
42548              * Fires before the textarea is updated with content from the editor iframe. Return false
42549              * to cancel the sync.
42550              * @param {HtmlEditor} this
42551              * @param {String} html
42552              */
42553             beforesync: true,
42554              /**
42555              * @event beforepush
42556              * Fires before the iframe editor is updated with content from the textarea. Return false
42557              * to cancel the push.
42558              * @param {HtmlEditor} this
42559              * @param {String} html
42560              */
42561             beforepush: true,
42562              /**
42563              * @event sync
42564              * Fires when the textarea is updated with content from the editor iframe.
42565              * @param {HtmlEditor} this
42566              * @param {String} html
42567              */
42568             sync: true,
42569              /**
42570              * @event push
42571              * Fires when the iframe editor is updated with content from the textarea.
42572              * @param {HtmlEditor} this
42573              * @param {String} html
42574              */
42575             push: true,
42576              /**
42577              * @event editmodechange
42578              * Fires when the editor switches edit modes
42579              * @param {HtmlEditor} this
42580              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42581              */
42582             editmodechange: true,
42583             /**
42584              * @event editorevent
42585              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42586              * @param {HtmlEditor} this
42587              */
42588             editorevent: true,
42589             /**
42590              * @event firstfocus
42591              * Fires when on first focus - needed by toolbars..
42592              * @param {HtmlEditor} this
42593              */
42594             firstfocus: true,
42595             /**
42596              * @event autosave
42597              * Auto save the htmlEditor value as a file into Events
42598              * @param {HtmlEditor} this
42599              */
42600             autosave: true,
42601             /**
42602              * @event savedpreview
42603              * preview the saved version of htmlEditor
42604              * @param {HtmlEditor} this
42605              */
42606             savedpreview: true
42607         });
42608         this.defaultAutoCreate =  {
42609             tag: "textarea",
42610             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42611             autocomplete: "off"
42612         };
42613     },
42614
42615     /**
42616      * Protected method that will not generally be called directly. It
42617      * is called when the editor creates its toolbar. Override this method if you need to
42618      * add custom toolbar buttons.
42619      * @param {HtmlEditor} editor
42620      */
42621     createToolbar : function(editor){
42622         Roo.log("create toolbars");
42623         if (!editor.toolbars || !editor.toolbars.length) {
42624             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42625         }
42626         
42627         for (var i =0 ; i < editor.toolbars.length;i++) {
42628             editor.toolbars[i] = Roo.factory(
42629                     typeof(editor.toolbars[i]) == 'string' ?
42630                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42631                 Roo.form.HtmlEditor);
42632             editor.toolbars[i].init(editor);
42633         }
42634          
42635         
42636     },
42637
42638      
42639     // private
42640     onRender : function(ct, position)
42641     {
42642         var _t = this;
42643         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42644         
42645         this.wrap = this.el.wrap({
42646             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42647         });
42648         
42649         this.editorcore.onRender(ct, position);
42650          
42651         if (this.resizable) {
42652             this.resizeEl = new Roo.Resizable(this.wrap, {
42653                 pinned : true,
42654                 wrap: true,
42655                 dynamic : true,
42656                 minHeight : this.height,
42657                 height: this.height,
42658                 handles : this.resizable,
42659                 width: this.width,
42660                 listeners : {
42661                     resize : function(r, w, h) {
42662                         _t.onResize(w,h); // -something
42663                     }
42664                 }
42665             });
42666             
42667         }
42668         this.createToolbar(this);
42669        
42670         
42671         if(!this.width){
42672             this.setSize(this.wrap.getSize());
42673         }
42674         if (this.resizeEl) {
42675             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42676             // should trigger onReize..
42677         }
42678         
42679 //        if(this.autosave && this.w){
42680 //            this.autoSaveFn = setInterval(this.autosave, 1000);
42681 //        }
42682     },
42683
42684     // private
42685     onResize : function(w, h)
42686     {
42687         //Roo.log('resize: ' +w + ',' + h );
42688         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
42689         var ew = false;
42690         var eh = false;
42691         
42692         if(this.el ){
42693             if(typeof w == 'number'){
42694                 var aw = w - this.wrap.getFrameWidth('lr');
42695                 this.el.setWidth(this.adjustWidth('textarea', aw));
42696                 ew = aw;
42697             }
42698             if(typeof h == 'number'){
42699                 var tbh = 0;
42700                 for (var i =0; i < this.toolbars.length;i++) {
42701                     // fixme - ask toolbars for heights?
42702                     tbh += this.toolbars[i].tb.el.getHeight();
42703                     if (this.toolbars[i].footer) {
42704                         tbh += this.toolbars[i].footer.el.getHeight();
42705                     }
42706                 }
42707                 
42708                 
42709                 
42710                 
42711                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
42712                 ah -= 5; // knock a few pixes off for look..
42713                 this.el.setHeight(this.adjustWidth('textarea', ah));
42714                 var eh = ah;
42715             }
42716         }
42717         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
42718         this.editorcore.onResize(ew,eh);
42719         
42720     },
42721
42722     /**
42723      * Toggles the editor between standard and source edit mode.
42724      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42725      */
42726     toggleSourceEdit : function(sourceEditMode)
42727     {
42728         this.editorcore.toggleSourceEdit(sourceEditMode);
42729         
42730         if(this.editorcore.sourceEditMode){
42731             Roo.log('editor - showing textarea');
42732             
42733 //            Roo.log('in');
42734 //            Roo.log(this.syncValue());
42735             this.editorcore.syncValue();
42736             this.el.removeClass('x-hidden');
42737             this.el.dom.removeAttribute('tabIndex');
42738             this.el.focus();
42739         }else{
42740             Roo.log('editor - hiding textarea');
42741 //            Roo.log('out')
42742 //            Roo.log(this.pushValue()); 
42743             this.editorcore.pushValue();
42744             
42745             this.el.addClass('x-hidden');
42746             this.el.dom.setAttribute('tabIndex', -1);
42747             //this.deferFocus();
42748         }
42749          
42750         this.setSize(this.wrap.getSize());
42751         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
42752     },
42753  
42754     // private (for BoxComponent)
42755     adjustSize : Roo.BoxComponent.prototype.adjustSize,
42756
42757     // private (for BoxComponent)
42758     getResizeEl : function(){
42759         return this.wrap;
42760     },
42761
42762     // private (for BoxComponent)
42763     getPositionEl : function(){
42764         return this.wrap;
42765     },
42766
42767     // private
42768     initEvents : function(){
42769         this.originalValue = this.getValue();
42770     },
42771
42772     /**
42773      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42774      * @method
42775      */
42776     markInvalid : Roo.emptyFn,
42777     /**
42778      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42779      * @method
42780      */
42781     clearInvalid : Roo.emptyFn,
42782
42783     setValue : function(v){
42784         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
42785         this.editorcore.pushValue();
42786     },
42787
42788      
42789     // private
42790     deferFocus : function(){
42791         this.focus.defer(10, this);
42792     },
42793
42794     // doc'ed in Field
42795     focus : function(){
42796         this.editorcore.focus();
42797         
42798     },
42799       
42800
42801     // private
42802     onDestroy : function(){
42803         
42804         
42805         
42806         if(this.rendered){
42807             
42808             for (var i =0; i < this.toolbars.length;i++) {
42809                 // fixme - ask toolbars for heights?
42810                 this.toolbars[i].onDestroy();
42811             }
42812             
42813             this.wrap.dom.innerHTML = '';
42814             this.wrap.remove();
42815         }
42816     },
42817
42818     // private
42819     onFirstFocus : function(){
42820         //Roo.log("onFirstFocus");
42821         this.editorcore.onFirstFocus();
42822          for (var i =0; i < this.toolbars.length;i++) {
42823             this.toolbars[i].onFirstFocus();
42824         }
42825         
42826     },
42827     
42828     // private
42829     syncValue : function()
42830     {
42831         this.editorcore.syncValue();
42832     },
42833     
42834     pushValue : function()
42835     {
42836         this.editorcore.pushValue();
42837     }
42838      
42839     
42840     // hide stuff that is not compatible
42841     /**
42842      * @event blur
42843      * @hide
42844      */
42845     /**
42846      * @event change
42847      * @hide
42848      */
42849     /**
42850      * @event focus
42851      * @hide
42852      */
42853     /**
42854      * @event specialkey
42855      * @hide
42856      */
42857     /**
42858      * @cfg {String} fieldClass @hide
42859      */
42860     /**
42861      * @cfg {String} focusClass @hide
42862      */
42863     /**
42864      * @cfg {String} autoCreate @hide
42865      */
42866     /**
42867      * @cfg {String} inputType @hide
42868      */
42869     /**
42870      * @cfg {String} invalidClass @hide
42871      */
42872     /**
42873      * @cfg {String} invalidText @hide
42874      */
42875     /**
42876      * @cfg {String} msgFx @hide
42877      */
42878     /**
42879      * @cfg {String} validateOnBlur @hide
42880      */
42881 });
42882  
42883     // <script type="text/javascript">
42884 /*
42885  * Based on
42886  * Ext JS Library 1.1.1
42887  * Copyright(c) 2006-2007, Ext JS, LLC.
42888  *  
42889  
42890  */
42891
42892 /**
42893  * @class Roo.form.HtmlEditorToolbar1
42894  * Basic Toolbar
42895  * 
42896  * Usage:
42897  *
42898  new Roo.form.HtmlEditor({
42899     ....
42900     toolbars : [
42901         new Roo.form.HtmlEditorToolbar1({
42902             disable : { fonts: 1 , format: 1, ..., ... , ...],
42903             btns : [ .... ]
42904         })
42905     }
42906      
42907  * 
42908  * @cfg {Object} disable List of elements to disable..
42909  * @cfg {Array} btns List of additional buttons.
42910  * 
42911  * 
42912  * NEEDS Extra CSS? 
42913  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42914  */
42915  
42916 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42917 {
42918     
42919     Roo.apply(this, config);
42920     
42921     // default disabled, based on 'good practice'..
42922     this.disable = this.disable || {};
42923     Roo.applyIf(this.disable, {
42924         fontSize : true,
42925         colors : true,
42926         specialElements : true
42927     });
42928     
42929     
42930     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42931     // dont call parent... till later.
42932 }
42933
42934 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42935     
42936     tb: false,
42937     
42938     rendered: false,
42939     
42940     editor : false,
42941     editorcore : false,
42942     /**
42943      * @cfg {Object} disable  List of toolbar elements to disable
42944          
42945      */
42946     disable : false,
42947     
42948     
42949      /**
42950      * @cfg {String} createLinkText The default text for the create link prompt
42951      */
42952     createLinkText : 'Please enter the URL for the link:',
42953     /**
42954      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
42955      */
42956     defaultLinkValue : 'http:/'+'/',
42957    
42958     
42959       /**
42960      * @cfg {Array} fontFamilies An array of available font families
42961      */
42962     fontFamilies : [
42963         'Arial',
42964         'Courier New',
42965         'Tahoma',
42966         'Times New Roman',
42967         'Verdana'
42968     ],
42969     
42970     specialChars : [
42971            "&#169;",
42972           "&#174;",     
42973           "&#8482;",    
42974           "&#163;" ,    
42975          // "&#8212;",    
42976           "&#8230;",    
42977           "&#247;" ,    
42978         //  "&#225;" ,     ?? a acute?
42979            "&#8364;"    , //Euro
42980        //   "&#8220;"    ,
42981         //  "&#8221;"    ,
42982         //  "&#8226;"    ,
42983           "&#176;"  //   , // degrees
42984
42985          // "&#233;"     , // e ecute
42986          // "&#250;"     , // u ecute?
42987     ],
42988     
42989     specialElements : [
42990         {
42991             text: "Insert Table",
42992             xtype: 'MenuItem',
42993             xns : Roo.Menu,
42994             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
42995                 
42996         },
42997         {    
42998             text: "Insert Image",
42999             xtype: 'MenuItem',
43000             xns : Roo.Menu,
43001             ihtml : '<img src="about:blank"/>'
43002             
43003         }
43004         
43005          
43006     ],
43007     
43008     
43009     inputElements : [ 
43010             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43011             "input:submit", "input:button", "select", "textarea", "label" ],
43012     formats : [
43013         ["p"] ,  
43014         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43015         ["pre"],[ "code"], 
43016         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43017         ['div'],['span']
43018     ],
43019     
43020     cleanStyles : [
43021         "font-size"
43022     ],
43023      /**
43024      * @cfg {String} defaultFont default font to use.
43025      */
43026     defaultFont: 'tahoma',
43027    
43028     fontSelect : false,
43029     
43030     
43031     formatCombo : false,
43032     
43033     init : function(editor)
43034     {
43035         this.editor = editor;
43036         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43037         var editorcore = this.editorcore;
43038         
43039         var _t = this;
43040         
43041         var fid = editorcore.frameId;
43042         var etb = this;
43043         function btn(id, toggle, handler){
43044             var xid = fid + '-'+ id ;
43045             return {
43046                 id : xid,
43047                 cmd : id,
43048                 cls : 'x-btn-icon x-edit-'+id,
43049                 enableToggle:toggle !== false,
43050                 scope: _t, // was editor...
43051                 handler:handler||_t.relayBtnCmd,
43052                 clickEvent:'mousedown',
43053                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43054                 tabIndex:-1
43055             };
43056         }
43057         
43058         
43059         
43060         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43061         this.tb = tb;
43062          // stop form submits
43063         tb.el.on('click', function(e){
43064             e.preventDefault(); // what does this do?
43065         });
43066
43067         if(!this.disable.font) { // && !Roo.isSafari){
43068             /* why no safari for fonts 
43069             editor.fontSelect = tb.el.createChild({
43070                 tag:'select',
43071                 tabIndex: -1,
43072                 cls:'x-font-select',
43073                 html: this.createFontOptions()
43074             });
43075             
43076             editor.fontSelect.on('change', function(){
43077                 var font = editor.fontSelect.dom.value;
43078                 editor.relayCmd('fontname', font);
43079                 editor.deferFocus();
43080             }, editor);
43081             
43082             tb.add(
43083                 editor.fontSelect.dom,
43084                 '-'
43085             );
43086             */
43087             
43088         };
43089         if(!this.disable.formats){
43090             this.formatCombo = new Roo.form.ComboBox({
43091                 store: new Roo.data.SimpleStore({
43092                     id : 'tag',
43093                     fields: ['tag'],
43094                     data : this.formats // from states.js
43095                 }),
43096                 blockFocus : true,
43097                 name : '',
43098                 //autoCreate : {tag: "div",  size: "20"},
43099                 displayField:'tag',
43100                 typeAhead: false,
43101                 mode: 'local',
43102                 editable : false,
43103                 triggerAction: 'all',
43104                 emptyText:'Add tag',
43105                 selectOnFocus:true,
43106                 width:135,
43107                 listeners : {
43108                     'select': function(c, r, i) {
43109                         editorcore.insertTag(r.get('tag'));
43110                         editor.focus();
43111                     }
43112                 }
43113
43114             });
43115             tb.addField(this.formatCombo);
43116             
43117         }
43118         
43119         if(!this.disable.format){
43120             tb.add(
43121                 btn('bold'),
43122                 btn('italic'),
43123                 btn('underline')
43124             );
43125         };
43126         if(!this.disable.fontSize){
43127             tb.add(
43128                 '-',
43129                 
43130                 
43131                 btn('increasefontsize', false, editorcore.adjustFont),
43132                 btn('decreasefontsize', false, editorcore.adjustFont)
43133             );
43134         };
43135         
43136         
43137         if(!this.disable.colors){
43138             tb.add(
43139                 '-', {
43140                     id:editorcore.frameId +'-forecolor',
43141                     cls:'x-btn-icon x-edit-forecolor',
43142                     clickEvent:'mousedown',
43143                     tooltip: this.buttonTips['forecolor'] || undefined,
43144                     tabIndex:-1,
43145                     menu : new Roo.menu.ColorMenu({
43146                         allowReselect: true,
43147                         focus: Roo.emptyFn,
43148                         value:'000000',
43149                         plain:true,
43150                         selectHandler: function(cp, color){
43151                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43152                             editor.deferFocus();
43153                         },
43154                         scope: editorcore,
43155                         clickEvent:'mousedown'
43156                     })
43157                 }, {
43158                     id:editorcore.frameId +'backcolor',
43159                     cls:'x-btn-icon x-edit-backcolor',
43160                     clickEvent:'mousedown',
43161                     tooltip: this.buttonTips['backcolor'] || undefined,
43162                     tabIndex:-1,
43163                     menu : new Roo.menu.ColorMenu({
43164                         focus: Roo.emptyFn,
43165                         value:'FFFFFF',
43166                         plain:true,
43167                         allowReselect: true,
43168                         selectHandler: function(cp, color){
43169                             if(Roo.isGecko){
43170                                 editorcore.execCmd('useCSS', false);
43171                                 editorcore.execCmd('hilitecolor', color);
43172                                 editorcore.execCmd('useCSS', true);
43173                                 editor.deferFocus();
43174                             }else{
43175                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43176                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43177                                 editor.deferFocus();
43178                             }
43179                         },
43180                         scope:editorcore,
43181                         clickEvent:'mousedown'
43182                     })
43183                 }
43184             );
43185         };
43186         // now add all the items...
43187         
43188
43189         if(!this.disable.alignments){
43190             tb.add(
43191                 '-',
43192                 btn('justifyleft'),
43193                 btn('justifycenter'),
43194                 btn('justifyright')
43195             );
43196         };
43197
43198         //if(!Roo.isSafari){
43199             if(!this.disable.links){
43200                 tb.add(
43201                     '-',
43202                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43203                 );
43204             };
43205
43206             if(!this.disable.lists){
43207                 tb.add(
43208                     '-',
43209                     btn('insertorderedlist'),
43210                     btn('insertunorderedlist')
43211                 );
43212             }
43213             if(!this.disable.sourceEdit){
43214                 tb.add(
43215                     '-',
43216                     btn('sourceedit', true, function(btn){
43217                         Roo.log(this);
43218                         this.toggleSourceEdit(btn.pressed);
43219                     })
43220                 );
43221             }
43222         //}
43223         
43224         var smenu = { };
43225         // special menu.. - needs to be tidied up..
43226         if (!this.disable.special) {
43227             smenu = {
43228                 text: "&#169;",
43229                 cls: 'x-edit-none',
43230                 
43231                 menu : {
43232                     items : []
43233                 }
43234             };
43235             for (var i =0; i < this.specialChars.length; i++) {
43236                 smenu.menu.items.push({
43237                     
43238                     html: this.specialChars[i],
43239                     handler: function(a,b) {
43240                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43241                         //editor.insertAtCursor(a.html);
43242                         
43243                     },
43244                     tabIndex:-1
43245                 });
43246             }
43247             
43248             
43249             tb.add(smenu);
43250             
43251             
43252         }
43253         
43254         var cmenu = { };
43255         if (!this.disable.cleanStyles) {
43256             cmenu = {
43257                 cls: 'x-btn-icon x-btn-clear',
43258                 
43259                 menu : {
43260                     items : []
43261                 }
43262             };
43263             for (var i =0; i < this.cleanStyles.length; i++) {
43264                 cmenu.menu.items.push({
43265                     actiontype : this.cleanStyles[i],
43266                     html: 'Remove ' + this.cleanStyles[i],
43267                     handler: function(a,b) {
43268                         Roo.log(a);
43269                         Roo.log(b);
43270                         var c = Roo.get(editorcore.doc.body);
43271                         c.select('[style]').each(function(s) {
43272                             s.dom.style.removeProperty(a.actiontype);
43273                         });
43274                         editorcore.syncValue();
43275                     },
43276                     tabIndex:-1
43277                 });
43278             }
43279             cmenu.menu.items.push({
43280                 actiontype : 'word',
43281                 html: 'Remove MS Word Formating',
43282                 handler: function(a,b) {
43283                     editorcore.cleanWord();
43284                     editorcore.syncValue();
43285                 },
43286                 tabIndex:-1
43287             });
43288             
43289             cmenu.menu.items.push({
43290                 actiontype : 'all',
43291                 html: 'Remove All Styles',
43292                 handler: function(a,b) {
43293                     
43294                     var c = Roo.get(editorcore.doc.body);
43295                     c.select('[style]').each(function(s) {
43296                         s.dom.removeAttribute('style');
43297                     });
43298                     editorcore.syncValue();
43299                 },
43300                 tabIndex:-1
43301             });
43302              cmenu.menu.items.push({
43303                 actiontype : 'word',
43304                 html: 'Tidy HTML Source',
43305                 handler: function(a,b) {
43306                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43307                     editorcore.syncValue();
43308                 },
43309                 tabIndex:-1
43310             });
43311             
43312             
43313             tb.add(cmenu);
43314         }
43315          
43316         if (!this.disable.specialElements) {
43317             var semenu = {
43318                 text: "Other;",
43319                 cls: 'x-edit-none',
43320                 menu : {
43321                     items : []
43322                 }
43323             };
43324             for (var i =0; i < this.specialElements.length; i++) {
43325                 semenu.menu.items.push(
43326                     Roo.apply({ 
43327                         handler: function(a,b) {
43328                             editor.insertAtCursor(this.ihtml);
43329                         }
43330                     }, this.specialElements[i])
43331                 );
43332                     
43333             }
43334             
43335             tb.add(semenu);
43336             
43337             
43338         }
43339          
43340         
43341         if (this.btns) {
43342             for(var i =0; i< this.btns.length;i++) {
43343                 var b = Roo.factory(this.btns[i],Roo.form);
43344                 b.cls =  'x-edit-none';
43345                 b.scope = editorcore;
43346                 tb.add(b);
43347             }
43348         
43349         }
43350         
43351         
43352         
43353         // disable everything...
43354         
43355         this.tb.items.each(function(item){
43356            if(item.id != editorcore.frameId+ '-sourceedit'){
43357                 item.disable();
43358             }
43359         });
43360         this.rendered = true;
43361         
43362         // the all the btns;
43363         editor.on('editorevent', this.updateToolbar, this);
43364         // other toolbars need to implement this..
43365         //editor.on('editmodechange', this.updateToolbar, this);
43366     },
43367     
43368     
43369     relayBtnCmd : function(btn) {
43370         this.editorcore.relayCmd(btn.cmd);
43371     },
43372     // private used internally
43373     createLink : function(){
43374         Roo.log("create link?");
43375         var url = prompt(this.createLinkText, this.defaultLinkValue);
43376         if(url && url != 'http:/'+'/'){
43377             this.editorcore.relayCmd('createlink', url);
43378         }
43379     },
43380
43381     
43382     /**
43383      * Protected method that will not generally be called directly. It triggers
43384      * a toolbar update by reading the markup state of the current selection in the editor.
43385      */
43386     updateToolbar: function(){
43387
43388         if(!this.editorcore.activated){
43389             this.editor.onFirstFocus();
43390             return;
43391         }
43392
43393         var btns = this.tb.items.map, 
43394             doc = this.editorcore.doc,
43395             frameId = this.editorcore.frameId;
43396
43397         if(!this.disable.font && !Roo.isSafari){
43398             /*
43399             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43400             if(name != this.fontSelect.dom.value){
43401                 this.fontSelect.dom.value = name;
43402             }
43403             */
43404         }
43405         if(!this.disable.format){
43406             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43407             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43408             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43409         }
43410         if(!this.disable.alignments){
43411             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43412             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43413             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43414         }
43415         if(!Roo.isSafari && !this.disable.lists){
43416             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43417             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43418         }
43419         
43420         var ans = this.editorcore.getAllAncestors();
43421         if (this.formatCombo) {
43422             
43423             
43424             var store = this.formatCombo.store;
43425             this.formatCombo.setValue("");
43426             for (var i =0; i < ans.length;i++) {
43427                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43428                     // select it..
43429                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43430                     break;
43431                 }
43432             }
43433         }
43434         
43435         
43436         
43437         // hides menus... - so this cant be on a menu...
43438         Roo.menu.MenuMgr.hideAll();
43439
43440         //this.editorsyncValue();
43441     },
43442    
43443     
43444     createFontOptions : function(){
43445         var buf = [], fs = this.fontFamilies, ff, lc;
43446         
43447         
43448         
43449         for(var i = 0, len = fs.length; i< len; i++){
43450             ff = fs[i];
43451             lc = ff.toLowerCase();
43452             buf.push(
43453                 '<option value="',lc,'" style="font-family:',ff,';"',
43454                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43455                     ff,
43456                 '</option>'
43457             );
43458         }
43459         return buf.join('');
43460     },
43461     
43462     toggleSourceEdit : function(sourceEditMode){
43463         
43464         Roo.log("toolbar toogle");
43465         if(sourceEditMode === undefined){
43466             sourceEditMode = !this.sourceEditMode;
43467         }
43468         this.sourceEditMode = sourceEditMode === true;
43469         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43470         // just toggle the button?
43471         if(btn.pressed !== this.sourceEditMode){
43472             btn.toggle(this.sourceEditMode);
43473             return;
43474         }
43475         
43476         if(sourceEditMode){
43477             Roo.log("disabling buttons");
43478             this.tb.items.each(function(item){
43479                 if(item.cmd != 'sourceedit'){
43480                     item.disable();
43481                 }
43482             });
43483           
43484         }else{
43485             Roo.log("enabling buttons");
43486             if(this.editorcore.initialized){
43487                 this.tb.items.each(function(item){
43488                     item.enable();
43489                 });
43490             }
43491             
43492         }
43493         Roo.log("calling toggole on editor");
43494         // tell the editor that it's been pressed..
43495         this.editor.toggleSourceEdit(sourceEditMode);
43496        
43497     },
43498      /**
43499      * Object collection of toolbar tooltips for the buttons in the editor. The key
43500      * is the command id associated with that button and the value is a valid QuickTips object.
43501      * For example:
43502 <pre><code>
43503 {
43504     bold : {
43505         title: 'Bold (Ctrl+B)',
43506         text: 'Make the selected text bold.',
43507         cls: 'x-html-editor-tip'
43508     },
43509     italic : {
43510         title: 'Italic (Ctrl+I)',
43511         text: 'Make the selected text italic.',
43512         cls: 'x-html-editor-tip'
43513     },
43514     ...
43515 </code></pre>
43516     * @type Object
43517      */
43518     buttonTips : {
43519         bold : {
43520             title: 'Bold (Ctrl+B)',
43521             text: 'Make the selected text bold.',
43522             cls: 'x-html-editor-tip'
43523         },
43524         italic : {
43525             title: 'Italic (Ctrl+I)',
43526             text: 'Make the selected text italic.',
43527             cls: 'x-html-editor-tip'
43528         },
43529         underline : {
43530             title: 'Underline (Ctrl+U)',
43531             text: 'Underline the selected text.',
43532             cls: 'x-html-editor-tip'
43533         },
43534         increasefontsize : {
43535             title: 'Grow Text',
43536             text: 'Increase the font size.',
43537             cls: 'x-html-editor-tip'
43538         },
43539         decreasefontsize : {
43540             title: 'Shrink Text',
43541             text: 'Decrease the font size.',
43542             cls: 'x-html-editor-tip'
43543         },
43544         backcolor : {
43545             title: 'Text Highlight Color',
43546             text: 'Change the background color of the selected text.',
43547             cls: 'x-html-editor-tip'
43548         },
43549         forecolor : {
43550             title: 'Font Color',
43551             text: 'Change the color of the selected text.',
43552             cls: 'x-html-editor-tip'
43553         },
43554         justifyleft : {
43555             title: 'Align Text Left',
43556             text: 'Align text to the left.',
43557             cls: 'x-html-editor-tip'
43558         },
43559         justifycenter : {
43560             title: 'Center Text',
43561             text: 'Center text in the editor.',
43562             cls: 'x-html-editor-tip'
43563         },
43564         justifyright : {
43565             title: 'Align Text Right',
43566             text: 'Align text to the right.',
43567             cls: 'x-html-editor-tip'
43568         },
43569         insertunorderedlist : {
43570             title: 'Bullet List',
43571             text: 'Start a bulleted list.',
43572             cls: 'x-html-editor-tip'
43573         },
43574         insertorderedlist : {
43575             title: 'Numbered List',
43576             text: 'Start a numbered list.',
43577             cls: 'x-html-editor-tip'
43578         },
43579         createlink : {
43580             title: 'Hyperlink',
43581             text: 'Make the selected text a hyperlink.',
43582             cls: 'x-html-editor-tip'
43583         },
43584         sourceedit : {
43585             title: 'Source Edit',
43586             text: 'Switch to source editing mode.',
43587             cls: 'x-html-editor-tip'
43588         }
43589     },
43590     // private
43591     onDestroy : function(){
43592         if(this.rendered){
43593             
43594             this.tb.items.each(function(item){
43595                 if(item.menu){
43596                     item.menu.removeAll();
43597                     if(item.menu.el){
43598                         item.menu.el.destroy();
43599                     }
43600                 }
43601                 item.destroy();
43602             });
43603              
43604         }
43605     },
43606     onFirstFocus: function() {
43607         this.tb.items.each(function(item){
43608            item.enable();
43609         });
43610     }
43611 });
43612
43613
43614
43615
43616 // <script type="text/javascript">
43617 /*
43618  * Based on
43619  * Ext JS Library 1.1.1
43620  * Copyright(c) 2006-2007, Ext JS, LLC.
43621  *  
43622  
43623  */
43624
43625  
43626 /**
43627  * @class Roo.form.HtmlEditor.ToolbarContext
43628  * Context Toolbar
43629  * 
43630  * Usage:
43631  *
43632  new Roo.form.HtmlEditor({
43633     ....
43634     toolbars : [
43635         { xtype: 'ToolbarStandard', styles : {} }
43636         { xtype: 'ToolbarContext', disable : {} }
43637     ]
43638 })
43639
43640      
43641  * 
43642  * @config : {Object} disable List of elements to disable.. (not done yet.)
43643  * @config : {Object} styles  Map of styles available.
43644  * 
43645  */
43646
43647 Roo.form.HtmlEditor.ToolbarContext = function(config)
43648 {
43649     
43650     Roo.apply(this, config);
43651     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43652     // dont call parent... till later.
43653     this.styles = this.styles || {};
43654 }
43655
43656  
43657
43658 Roo.form.HtmlEditor.ToolbarContext.types = {
43659     'IMG' : {
43660         width : {
43661             title: "Width",
43662             width: 40
43663         },
43664         height:  {
43665             title: "Height",
43666             width: 40
43667         },
43668         align: {
43669             title: "Align",
43670             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
43671             width : 80
43672             
43673         },
43674         border: {
43675             title: "Border",
43676             width: 40
43677         },
43678         alt: {
43679             title: "Alt",
43680             width: 120
43681         },
43682         src : {
43683             title: "Src",
43684             width: 220
43685         }
43686         
43687     },
43688     'A' : {
43689         name : {
43690             title: "Name",
43691             width: 50
43692         },
43693         target:  {
43694             title: "Target",
43695             width: 120
43696         },
43697         href:  {
43698             title: "Href",
43699             width: 220
43700         } // border?
43701         
43702     },
43703     'TABLE' : {
43704         rows : {
43705             title: "Rows",
43706             width: 20
43707         },
43708         cols : {
43709             title: "Cols",
43710             width: 20
43711         },
43712         width : {
43713             title: "Width",
43714             width: 40
43715         },
43716         height : {
43717             title: "Height",
43718             width: 40
43719         },
43720         border : {
43721             title: "Border",
43722             width: 20
43723         }
43724     },
43725     'TD' : {
43726         width : {
43727             title: "Width",
43728             width: 40
43729         },
43730         height : {
43731             title: "Height",
43732             width: 40
43733         },   
43734         align: {
43735             title: "Align",
43736             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
43737             width: 80
43738         },
43739         valign: {
43740             title: "Valign",
43741             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43742             width: 80
43743         },
43744         colspan: {
43745             title: "Colspan",
43746             width: 20
43747             
43748         },
43749          'font-family'  : {
43750             title : "Font",
43751             style : 'fontFamily',
43752             displayField: 'display',
43753             optname : 'font-family',
43754             width: 140
43755         }
43756     },
43757     'INPUT' : {
43758         name : {
43759             title: "name",
43760             width: 120
43761         },
43762         value : {
43763             title: "Value",
43764             width: 120
43765         },
43766         width : {
43767             title: "Width",
43768             width: 40
43769         }
43770     },
43771     'LABEL' : {
43772         'for' : {
43773             title: "For",
43774             width: 120
43775         }
43776     },
43777     'TEXTAREA' : {
43778           name : {
43779             title: "name",
43780             width: 120
43781         },
43782         rows : {
43783             title: "Rows",
43784             width: 20
43785         },
43786         cols : {
43787             title: "Cols",
43788             width: 20
43789         }
43790     },
43791     'SELECT' : {
43792         name : {
43793             title: "name",
43794             width: 120
43795         },
43796         selectoptions : {
43797             title: "Options",
43798             width: 200
43799         }
43800     },
43801     
43802     // should we really allow this??
43803     // should this just be 
43804     'BODY' : {
43805         title : {
43806             title: "Title",
43807             width: 200,
43808             disabled : true
43809         }
43810     },
43811     'SPAN' : {
43812         'font-family'  : {
43813             title : "Font",
43814             style : 'fontFamily',
43815             displayField: 'display',
43816             optname : 'font-family',
43817             width: 140
43818         }
43819     },
43820     'DIV' : {
43821         'font-family'  : {
43822             title : "Font",
43823             style : 'fontFamily',
43824             displayField: 'display',
43825             optname : 'font-family',
43826             width: 140
43827         }
43828     },
43829      'P' : {
43830         'font-family'  : {
43831             title : "Font",
43832             style : 'fontFamily',
43833             displayField: 'display',
43834             optname : 'font-family',
43835             width: 140
43836         }
43837     },
43838     
43839     '*' : {
43840         // empty..
43841     }
43842
43843 };
43844
43845 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43846 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43847
43848 Roo.form.HtmlEditor.ToolbarContext.options = {
43849         'font-family'  : [ 
43850                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43851                 [ 'Courier New', 'Courier New'],
43852                 [ 'Tahoma', 'Tahoma'],
43853                 [ 'Times New Roman,serif', 'Times'],
43854                 [ 'Verdana','Verdana' ]
43855         ]
43856 };
43857
43858 // fixme - these need to be configurable..
43859  
43860
43861 Roo.form.HtmlEditor.ToolbarContext.types
43862
43863
43864 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43865     
43866     tb: false,
43867     
43868     rendered: false,
43869     
43870     editor : false,
43871     editorcore : false,
43872     /**
43873      * @cfg {Object} disable  List of toolbar elements to disable
43874          
43875      */
43876     disable : false,
43877     /**
43878      * @cfg {Object} styles List of styles 
43879      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43880      *
43881      * These must be defined in the page, so they get rendered correctly..
43882      * .headline { }
43883      * TD.underline { }
43884      * 
43885      */
43886     styles : false,
43887     
43888     options: false,
43889     
43890     toolbars : false,
43891     
43892     init : function(editor)
43893     {
43894         this.editor = editor;
43895         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43896         var editorcore = this.editorcore;
43897         
43898         var fid = editorcore.frameId;
43899         var etb = this;
43900         function btn(id, toggle, handler){
43901             var xid = fid + '-'+ id ;
43902             return {
43903                 id : xid,
43904                 cmd : id,
43905                 cls : 'x-btn-icon x-edit-'+id,
43906                 enableToggle:toggle !== false,
43907                 scope: editorcore, // was editor...
43908                 handler:handler||editorcore.relayBtnCmd,
43909                 clickEvent:'mousedown',
43910                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43911                 tabIndex:-1
43912             };
43913         }
43914         // create a new element.
43915         var wdiv = editor.wrap.createChild({
43916                 tag: 'div'
43917             }, editor.wrap.dom.firstChild.nextSibling, true);
43918         
43919         // can we do this more than once??
43920         
43921          // stop form submits
43922       
43923  
43924         // disable everything...
43925         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43926         this.toolbars = {};
43927            
43928         for (var i in  ty) {
43929           
43930             this.toolbars[i] = this.buildToolbar(ty[i],i);
43931         }
43932         this.tb = this.toolbars.BODY;
43933         this.tb.el.show();
43934         this.buildFooter();
43935         this.footer.show();
43936         editor.on('hide', function( ) { this.footer.hide() }, this);
43937         editor.on('show', function( ) { this.footer.show() }, this);
43938         
43939          
43940         this.rendered = true;
43941         
43942         // the all the btns;
43943         editor.on('editorevent', this.updateToolbar, this);
43944         // other toolbars need to implement this..
43945         //editor.on('editmodechange', this.updateToolbar, this);
43946     },
43947     
43948     
43949     
43950     /**
43951      * Protected method that will not generally be called directly. It triggers
43952      * a toolbar update by reading the markup state of the current selection in the editor.
43953      */
43954     updateToolbar: function(editor,ev,sel){
43955
43956         //Roo.log(ev);
43957         // capture mouse up - this is handy for selecting images..
43958         // perhaps should go somewhere else...
43959         if(!this.editorcore.activated){
43960              this.editor.onFirstFocus();
43961             return;
43962         }
43963         
43964         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43965         // selectNode - might want to handle IE?
43966         if (ev &&
43967             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43968             ev.target && ev.target.tagName == 'IMG') {
43969             // they have click on an image...
43970             // let's see if we can change the selection...
43971             sel = ev.target;
43972          
43973               var nodeRange = sel.ownerDocument.createRange();
43974             try {
43975                 nodeRange.selectNode(sel);
43976             } catch (e) {
43977                 nodeRange.selectNodeContents(sel);
43978             }
43979             //nodeRange.collapse(true);
43980             var s = this.editorcore.win.getSelection();
43981             s.removeAllRanges();
43982             s.addRange(nodeRange);
43983         }  
43984         
43985       
43986         var updateFooter = sel ? false : true;
43987         
43988         
43989         var ans = this.editorcore.getAllAncestors();
43990         
43991         // pick
43992         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43993         
43994         if (!sel) { 
43995             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
43996             sel = sel ? sel : this.editorcore.doc.body;
43997             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
43998             
43999         }
44000         // pick a menu that exists..
44001         var tn = sel.tagName.toUpperCase();
44002         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44003         
44004         tn = sel.tagName.toUpperCase();
44005         
44006         var lastSel = this.tb.selectedNode
44007         
44008         this.tb.selectedNode = sel;
44009         
44010         // if current menu does not match..
44011         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
44012                 
44013             this.tb.el.hide();
44014             ///console.log("show: " + tn);
44015             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44016             this.tb.el.show();
44017             // update name
44018             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44019             
44020             
44021             // update attributes
44022             if (this.tb.fields) {
44023                 this.tb.fields.each(function(e) {
44024                     if (e.stylename) {
44025                         e.setValue(sel.style[e.stylename]);
44026                         return;
44027                     } 
44028                    e.setValue(sel.getAttribute(e.attrname));
44029                 });
44030             }
44031             
44032             var hasStyles = false;
44033             for(var i in this.styles) {
44034                 hasStyles = true;
44035                 break;
44036             }
44037             
44038             // update styles
44039             if (hasStyles) { 
44040                 var st = this.tb.fields.item(0);
44041                 
44042                 st.store.removeAll();
44043                
44044                 
44045                 var cn = sel.className.split(/\s+/);
44046                 
44047                 var avs = [];
44048                 if (this.styles['*']) {
44049                     
44050                     Roo.each(this.styles['*'], function(v) {
44051                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44052                     });
44053                 }
44054                 if (this.styles[tn]) { 
44055                     Roo.each(this.styles[tn], function(v) {
44056                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44057                     });
44058                 }
44059                 
44060                 st.store.loadData(avs);
44061                 st.collapse();
44062                 st.setValue(cn);
44063             }
44064             // flag our selected Node.
44065             this.tb.selectedNode = sel;
44066            
44067            
44068             Roo.menu.MenuMgr.hideAll();
44069
44070         }
44071         
44072         if (!updateFooter) {
44073             //this.footDisp.dom.innerHTML = ''; 
44074             return;
44075         }
44076         // update the footer
44077         //
44078         var html = '';
44079         
44080         this.footerEls = ans.reverse();
44081         Roo.each(this.footerEls, function(a,i) {
44082             if (!a) { return; }
44083             html += html.length ? ' &gt; '  :  '';
44084             
44085             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44086             
44087         });
44088        
44089         // 
44090         var sz = this.footDisp.up('td').getSize();
44091         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44092         this.footDisp.dom.style.marginLeft = '5px';
44093         
44094         this.footDisp.dom.style.overflow = 'hidden';
44095         
44096         this.footDisp.dom.innerHTML = html;
44097             
44098         //this.editorsyncValue();
44099     },
44100      
44101     
44102    
44103        
44104     // private
44105     onDestroy : function(){
44106         if(this.rendered){
44107             
44108             this.tb.items.each(function(item){
44109                 if(item.menu){
44110                     item.menu.removeAll();
44111                     if(item.menu.el){
44112                         item.menu.el.destroy();
44113                     }
44114                 }
44115                 item.destroy();
44116             });
44117              
44118         }
44119     },
44120     onFirstFocus: function() {
44121         // need to do this for all the toolbars..
44122         this.tb.items.each(function(item){
44123            item.enable();
44124         });
44125     },
44126     buildToolbar: function(tlist, nm)
44127     {
44128         var editor = this.editor;
44129         var editorcore = this.editorcore;
44130          // create a new element.
44131         var wdiv = editor.wrap.createChild({
44132                 tag: 'div'
44133             }, editor.wrap.dom.firstChild.nextSibling, true);
44134         
44135        
44136         var tb = new Roo.Toolbar(wdiv);
44137         // add the name..
44138         
44139         tb.add(nm+ ":&nbsp;");
44140         
44141         var styles = [];
44142         for(var i in this.styles) {
44143             styles.push(i);
44144         }
44145         
44146         // styles...
44147         if (styles && styles.length) {
44148             
44149             // this needs a multi-select checkbox...
44150             tb.addField( new Roo.form.ComboBox({
44151                 store: new Roo.data.SimpleStore({
44152                     id : 'val',
44153                     fields: ['val', 'selected'],
44154                     data : [] 
44155                 }),
44156                 name : '-roo-edit-className',
44157                 attrname : 'className',
44158                 displayField: 'val',
44159                 typeAhead: false,
44160                 mode: 'local',
44161                 editable : false,
44162                 triggerAction: 'all',
44163                 emptyText:'Select Style',
44164                 selectOnFocus:true,
44165                 width: 130,
44166                 listeners : {
44167                     'select': function(c, r, i) {
44168                         // initial support only for on class per el..
44169                         tb.selectedNode.className =  r ? r.get('val') : '';
44170                         editorcore.syncValue();
44171                     }
44172                 }
44173     
44174             }));
44175         }
44176         
44177         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44178         var tbops = tbc.options;
44179         
44180         for (var i in tlist) {
44181             
44182             var item = tlist[i];
44183             tb.add(item.title + ":&nbsp;");
44184             
44185             
44186             //optname == used so you can configure the options available..
44187             var opts = item.opts ? item.opts : false;
44188             if (item.optname) {
44189                 opts = tbops[item.optname];
44190            
44191             }
44192             
44193             if (opts) {
44194                 // opts == pulldown..
44195                 tb.addField( new Roo.form.ComboBox({
44196                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44197                         id : 'val',
44198                         fields: ['val', 'display'],
44199                         data : opts  
44200                     }),
44201                     name : '-roo-edit-' + i,
44202                     attrname : i,
44203                     stylename : item.style ? item.style : false,
44204                     displayField: item.displayField ? item.displayField : 'val',
44205                     valueField :  'val',
44206                     typeAhead: false,
44207                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44208                     editable : false,
44209                     triggerAction: 'all',
44210                     emptyText:'Select',
44211                     selectOnFocus:true,
44212                     width: item.width ? item.width  : 130,
44213                     listeners : {
44214                         'select': function(c, r, i) {
44215                             if (c.stylename) {
44216                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44217                                 return;
44218                             }
44219                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44220                         }
44221                     }
44222
44223                 }));
44224                 continue;
44225                     
44226                  
44227                 
44228                 tb.addField( new Roo.form.TextField({
44229                     name: i,
44230                     width: 100,
44231                     //allowBlank:false,
44232                     value: ''
44233                 }));
44234                 continue;
44235             }
44236             tb.addField( new Roo.form.TextField({
44237                 name: '-roo-edit-' + i,
44238                 attrname : i,
44239                 
44240                 width: item.width,
44241                 //allowBlank:true,
44242                 value: '',
44243                 listeners: {
44244                     'change' : function(f, nv, ov) {
44245                         tb.selectedNode.setAttribute(f.attrname, nv);
44246                     }
44247                 }
44248             }));
44249              
44250         }
44251         tb.addFill();
44252         var _this = this;
44253         tb.addButton( {
44254             text: 'Remove Tag',
44255     
44256             listeners : {
44257                 click : function ()
44258                 {
44259                     // remove
44260                     // undo does not work.
44261                      
44262                     var sn = tb.selectedNode;
44263                     
44264                     var pn = sn.parentNode;
44265                     
44266                     var stn =  sn.childNodes[0];
44267                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44268                     while (sn.childNodes.length) {
44269                         var node = sn.childNodes[0];
44270                         sn.removeChild(node);
44271                         //Roo.log(node);
44272                         pn.insertBefore(node, sn);
44273                         
44274                     }
44275                     pn.removeChild(sn);
44276                     var range = editorcore.createRange();
44277         
44278                     range.setStart(stn,0);
44279                     range.setEnd(en,0); //????
44280                     //range.selectNode(sel);
44281                     
44282                     
44283                     var selection = editorcore.getSelection();
44284                     selection.removeAllRanges();
44285                     selection.addRange(range);
44286                     
44287                     
44288                     
44289                     //_this.updateToolbar(null, null, pn);
44290                     _this.updateToolbar(null, null, null);
44291                     _this.footDisp.dom.innerHTML = ''; 
44292                 }
44293             }
44294             
44295                     
44296                 
44297             
44298         });
44299         
44300         
44301         tb.el.on('click', function(e){
44302             e.preventDefault(); // what does this do?
44303         });
44304         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44305         tb.el.hide();
44306         tb.name = nm;
44307         // dont need to disable them... as they will get hidden
44308         return tb;
44309          
44310         
44311     },
44312     buildFooter : function()
44313     {
44314         
44315         var fel = this.editor.wrap.createChild();
44316         this.footer = new Roo.Toolbar(fel);
44317         // toolbar has scrolly on left / right?
44318         var footDisp= new Roo.Toolbar.Fill();
44319         var _t = this;
44320         this.footer.add(
44321             {
44322                 text : '&lt;',
44323                 xtype: 'Button',
44324                 handler : function() {
44325                     _t.footDisp.scrollTo('left',0,true)
44326                 }
44327             }
44328         );
44329         this.footer.add( footDisp );
44330         this.footer.add( 
44331             {
44332                 text : '&gt;',
44333                 xtype: 'Button',
44334                 handler : function() {
44335                     // no animation..
44336                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44337                 }
44338             }
44339         );
44340         var fel = Roo.get(footDisp.el);
44341         fel.addClass('x-editor-context');
44342         this.footDispWrap = fel; 
44343         this.footDispWrap.overflow  = 'hidden';
44344         
44345         this.footDisp = fel.createChild();
44346         this.footDispWrap.on('click', this.onContextClick, this)
44347         
44348         
44349     },
44350     onContextClick : function (ev,dom)
44351     {
44352         ev.preventDefault();
44353         var  cn = dom.className;
44354         //Roo.log(cn);
44355         if (!cn.match(/x-ed-loc-/)) {
44356             return;
44357         }
44358         var n = cn.split('-').pop();
44359         var ans = this.footerEls;
44360         var sel = ans[n];
44361         
44362          // pick
44363         var range = this.editorcore.createRange();
44364         
44365         range.selectNodeContents(sel);
44366         //range.selectNode(sel);
44367         
44368         
44369         var selection = this.editorcore.getSelection();
44370         selection.removeAllRanges();
44371         selection.addRange(range);
44372         
44373         
44374         
44375         this.updateToolbar(null, null, sel);
44376         
44377         
44378     }
44379     
44380     
44381     
44382     
44383     
44384 });
44385
44386
44387
44388
44389
44390 /*
44391  * Based on:
44392  * Ext JS Library 1.1.1
44393  * Copyright(c) 2006-2007, Ext JS, LLC.
44394  *
44395  * Originally Released Under LGPL - original licence link has changed is not relivant.
44396  *
44397  * Fork - LGPL
44398  * <script type="text/javascript">
44399  */
44400  
44401 /**
44402  * @class Roo.form.BasicForm
44403  * @extends Roo.util.Observable
44404  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44405  * @constructor
44406  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44407  * @param {Object} config Configuration options
44408  */
44409 Roo.form.BasicForm = function(el, config){
44410     this.allItems = [];
44411     this.childForms = [];
44412     Roo.apply(this, config);
44413     /*
44414      * The Roo.form.Field items in this form.
44415      * @type MixedCollection
44416      */
44417      
44418      
44419     this.items = new Roo.util.MixedCollection(false, function(o){
44420         return o.id || (o.id = Roo.id());
44421     });
44422     this.addEvents({
44423         /**
44424          * @event beforeaction
44425          * Fires before any action is performed. Return false to cancel the action.
44426          * @param {Form} this
44427          * @param {Action} action The action to be performed
44428          */
44429         beforeaction: true,
44430         /**
44431          * @event actionfailed
44432          * Fires when an action fails.
44433          * @param {Form} this
44434          * @param {Action} action The action that failed
44435          */
44436         actionfailed : true,
44437         /**
44438          * @event actioncomplete
44439          * Fires when an action is completed.
44440          * @param {Form} this
44441          * @param {Action} action The action that completed
44442          */
44443         actioncomplete : true
44444     });
44445     if(el){
44446         this.initEl(el);
44447     }
44448     Roo.form.BasicForm.superclass.constructor.call(this);
44449 };
44450
44451 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44452     /**
44453      * @cfg {String} method
44454      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44455      */
44456     /**
44457      * @cfg {DataReader} reader
44458      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44459      * This is optional as there is built-in support for processing JSON.
44460      */
44461     /**
44462      * @cfg {DataReader} errorReader
44463      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44464      * This is completely optional as there is built-in support for processing JSON.
44465      */
44466     /**
44467      * @cfg {String} url
44468      * The URL to use for form actions if one isn't supplied in the action options.
44469      */
44470     /**
44471      * @cfg {Boolean} fileUpload
44472      * Set to true if this form is a file upload.
44473      */
44474      
44475     /**
44476      * @cfg {Object} baseParams
44477      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44478      */
44479      /**
44480      
44481     /**
44482      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44483      */
44484     timeout: 30,
44485
44486     // private
44487     activeAction : null,
44488
44489     /**
44490      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44491      * or setValues() data instead of when the form was first created.
44492      */
44493     trackResetOnLoad : false,
44494     
44495     
44496     /**
44497      * childForms - used for multi-tab forms
44498      * @type {Array}
44499      */
44500     childForms : false,
44501     
44502     /**
44503      * allItems - full list of fields.
44504      * @type {Array}
44505      */
44506     allItems : false,
44507     
44508     /**
44509      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44510      * element by passing it or its id or mask the form itself by passing in true.
44511      * @type Mixed
44512      */
44513     waitMsgTarget : false,
44514
44515     // private
44516     initEl : function(el){
44517         this.el = Roo.get(el);
44518         this.id = this.el.id || Roo.id();
44519         this.el.on('submit', this.onSubmit, this);
44520         this.el.addClass('x-form');
44521     },
44522
44523     // private
44524     onSubmit : function(e){
44525         e.stopEvent();
44526     },
44527
44528     /**
44529      * Returns true if client-side validation on the form is successful.
44530      * @return Boolean
44531      */
44532     isValid : function(){
44533         var valid = true;
44534         this.items.each(function(f){
44535            if(!f.validate()){
44536                valid = false;
44537            }
44538         });
44539         return valid;
44540     },
44541
44542     /**
44543      * Returns true if any fields in this form have changed since their original load.
44544      * @return Boolean
44545      */
44546     isDirty : function(){
44547         var dirty = false;
44548         this.items.each(function(f){
44549            if(f.isDirty()){
44550                dirty = true;
44551                return false;
44552            }
44553         });
44554         return dirty;
44555     },
44556
44557     /**
44558      * Performs a predefined action (submit or load) or custom actions you define on this form.
44559      * @param {String} actionName The name of the action type
44560      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
44561      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
44562      * accept other config options):
44563      * <pre>
44564 Property          Type             Description
44565 ----------------  ---------------  ----------------------------------------------------------------------------------
44566 url               String           The url for the action (defaults to the form's url)
44567 method            String           The form method to use (defaults to the form's method, or POST if not defined)
44568 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
44569 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
44570                                    validate the form on the client (defaults to false)
44571      * </pre>
44572      * @return {BasicForm} this
44573      */
44574     doAction : function(action, options){
44575         if(typeof action == 'string'){
44576             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
44577         }
44578         if(this.fireEvent('beforeaction', this, action) !== false){
44579             this.beforeAction(action);
44580             action.run.defer(100, action);
44581         }
44582         return this;
44583     },
44584
44585     /**
44586      * Shortcut to do a submit action.
44587      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44588      * @return {BasicForm} this
44589      */
44590     submit : function(options){
44591         this.doAction('submit', options);
44592         return this;
44593     },
44594
44595     /**
44596      * Shortcut to do a load action.
44597      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44598      * @return {BasicForm} this
44599      */
44600     load : function(options){
44601         this.doAction('load', options);
44602         return this;
44603     },
44604
44605     /**
44606      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
44607      * @param {Record} record The record to edit
44608      * @return {BasicForm} this
44609      */
44610     updateRecord : function(record){
44611         record.beginEdit();
44612         var fs = record.fields;
44613         fs.each(function(f){
44614             var field = this.findField(f.name);
44615             if(field){
44616                 record.set(f.name, field.getValue());
44617             }
44618         }, this);
44619         record.endEdit();
44620         return this;
44621     },
44622
44623     /**
44624      * Loads an Roo.data.Record into this form.
44625      * @param {Record} record The record to load
44626      * @return {BasicForm} this
44627      */
44628     loadRecord : function(record){
44629         this.setValues(record.data);
44630         return this;
44631     },
44632
44633     // private
44634     beforeAction : function(action){
44635         var o = action.options;
44636         
44637        
44638         if(this.waitMsgTarget === true){
44639             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
44640         }else if(this.waitMsgTarget){
44641             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
44642             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
44643         }else {
44644             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
44645         }
44646          
44647     },
44648
44649     // private
44650     afterAction : function(action, success){
44651         this.activeAction = null;
44652         var o = action.options;
44653         
44654         if(this.waitMsgTarget === true){
44655             this.el.unmask();
44656         }else if(this.waitMsgTarget){
44657             this.waitMsgTarget.unmask();
44658         }else{
44659             Roo.MessageBox.updateProgress(1);
44660             Roo.MessageBox.hide();
44661         }
44662          
44663         if(success){
44664             if(o.reset){
44665                 this.reset();
44666             }
44667             Roo.callback(o.success, o.scope, [this, action]);
44668             this.fireEvent('actioncomplete', this, action);
44669             
44670         }else{
44671             
44672             // failure condition..
44673             // we have a scenario where updates need confirming.
44674             // eg. if a locking scenario exists..
44675             // we look for { errors : { needs_confirm : true }} in the response.
44676             if (
44677                 (typeof(action.result) != 'undefined')  &&
44678                 (typeof(action.result.errors) != 'undefined')  &&
44679                 (typeof(action.result.errors.needs_confirm) != 'undefined')
44680            ){
44681                 var _t = this;
44682                 Roo.MessageBox.confirm(
44683                     "Change requires confirmation",
44684                     action.result.errorMsg,
44685                     function(r) {
44686                         if (r != 'yes') {
44687                             return;
44688                         }
44689                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
44690                     }
44691                     
44692                 );
44693                 
44694                 
44695                 
44696                 return;
44697             }
44698             
44699             Roo.callback(o.failure, o.scope, [this, action]);
44700             // show an error message if no failed handler is set..
44701             if (!this.hasListener('actionfailed')) {
44702                 Roo.MessageBox.alert("Error",
44703                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
44704                         action.result.errorMsg :
44705                         "Saving Failed, please check your entries or try again"
44706                 );
44707             }
44708             
44709             this.fireEvent('actionfailed', this, action);
44710         }
44711         
44712     },
44713
44714     /**
44715      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
44716      * @param {String} id The value to search for
44717      * @return Field
44718      */
44719     findField : function(id){
44720         var field = this.items.get(id);
44721         if(!field){
44722             this.items.each(function(f){
44723                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
44724                     field = f;
44725                     return false;
44726                 }
44727             });
44728         }
44729         return field || null;
44730     },
44731
44732     /**
44733      * Add a secondary form to this one, 
44734      * Used to provide tabbed forms. One form is primary, with hidden values 
44735      * which mirror the elements from the other forms.
44736      * 
44737      * @param {Roo.form.Form} form to add.
44738      * 
44739      */
44740     addForm : function(form)
44741     {
44742        
44743         if (this.childForms.indexOf(form) > -1) {
44744             // already added..
44745             return;
44746         }
44747         this.childForms.push(form);
44748         var n = '';
44749         Roo.each(form.allItems, function (fe) {
44750             
44751             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44752             if (this.findField(n)) { // already added..
44753                 return;
44754             }
44755             var add = new Roo.form.Hidden({
44756                 name : n
44757             });
44758             add.render(this.el);
44759             
44760             this.add( add );
44761         }, this);
44762         
44763     },
44764     /**
44765      * Mark fields in this form invalid in bulk.
44766      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44767      * @return {BasicForm} this
44768      */
44769     markInvalid : function(errors){
44770         if(errors instanceof Array){
44771             for(var i = 0, len = errors.length; i < len; i++){
44772                 var fieldError = errors[i];
44773                 var f = this.findField(fieldError.id);
44774                 if(f){
44775                     f.markInvalid(fieldError.msg);
44776                 }
44777             }
44778         }else{
44779             var field, id;
44780             for(id in errors){
44781                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44782                     field.markInvalid(errors[id]);
44783                 }
44784             }
44785         }
44786         Roo.each(this.childForms || [], function (f) {
44787             f.markInvalid(errors);
44788         });
44789         
44790         return this;
44791     },
44792
44793     /**
44794      * Set values for fields in this form in bulk.
44795      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44796      * @return {BasicForm} this
44797      */
44798     setValues : function(values){
44799         if(values instanceof Array){ // array of objects
44800             for(var i = 0, len = values.length; i < len; i++){
44801                 var v = values[i];
44802                 var f = this.findField(v.id);
44803                 if(f){
44804                     f.setValue(v.value);
44805                     if(this.trackResetOnLoad){
44806                         f.originalValue = f.getValue();
44807                     }
44808                 }
44809             }
44810         }else{ // object hash
44811             var field, id;
44812             for(id in values){
44813                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44814                     
44815                     if (field.setFromData && 
44816                         field.valueField && 
44817                         field.displayField &&
44818                         // combos' with local stores can 
44819                         // be queried via setValue()
44820                         // to set their value..
44821                         (field.store && !field.store.isLocal)
44822                         ) {
44823                         // it's a combo
44824                         var sd = { };
44825                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44826                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44827                         field.setFromData(sd);
44828                         
44829                     } else {
44830                         field.setValue(values[id]);
44831                     }
44832                     
44833                     
44834                     if(this.trackResetOnLoad){
44835                         field.originalValue = field.getValue();
44836                     }
44837                 }
44838             }
44839         }
44840          
44841         Roo.each(this.childForms || [], function (f) {
44842             f.setValues(values);
44843         });
44844                 
44845         return this;
44846     },
44847
44848     /**
44849      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44850      * they are returned as an array.
44851      * @param {Boolean} asString
44852      * @return {Object}
44853      */
44854     getValues : function(asString){
44855         if (this.childForms) {
44856             // copy values from the child forms
44857             Roo.each(this.childForms, function (f) {
44858                 this.setValues(f.getValues());
44859             }, this);
44860         }
44861         
44862         
44863         
44864         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44865         if(asString === true){
44866             return fs;
44867         }
44868         return Roo.urlDecode(fs);
44869     },
44870     
44871     /**
44872      * Returns the fields in this form as an object with key/value pairs. 
44873      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44874      * @return {Object}
44875      */
44876     getFieldValues : function(with_hidden)
44877     {
44878         if (this.childForms) {
44879             // copy values from the child forms
44880             // should this call getFieldValues - probably not as we do not currently copy
44881             // hidden fields when we generate..
44882             Roo.each(this.childForms, function (f) {
44883                 this.setValues(f.getValues());
44884             }, this);
44885         }
44886         
44887         var ret = {};
44888         this.items.each(function(f){
44889             if (!f.getName()) {
44890                 return;
44891             }
44892             var v = f.getValue();
44893             if (f.inputType =='radio') {
44894                 if (typeof(ret[f.getName()]) == 'undefined') {
44895                     ret[f.getName()] = ''; // empty..
44896                 }
44897                 
44898                 if (!f.el.dom.checked) {
44899                     return;
44900                     
44901                 }
44902                 v = f.el.dom.value;
44903                 
44904             }
44905             
44906             // not sure if this supported any more..
44907             if ((typeof(v) == 'object') && f.getRawValue) {
44908                 v = f.getRawValue() ; // dates..
44909             }
44910             // combo boxes where name != hiddenName...
44911             if (f.name != f.getName()) {
44912                 ret[f.name] = f.getRawValue();
44913             }
44914             ret[f.getName()] = v;
44915         });
44916         
44917         return ret;
44918     },
44919
44920     /**
44921      * Clears all invalid messages in this form.
44922      * @return {BasicForm} this
44923      */
44924     clearInvalid : function(){
44925         this.items.each(function(f){
44926            f.clearInvalid();
44927         });
44928         
44929         Roo.each(this.childForms || [], function (f) {
44930             f.clearInvalid();
44931         });
44932         
44933         
44934         return this;
44935     },
44936
44937     /**
44938      * Resets this form.
44939      * @return {BasicForm} this
44940      */
44941     reset : function(){
44942         this.items.each(function(f){
44943             f.reset();
44944         });
44945         
44946         Roo.each(this.childForms || [], function (f) {
44947             f.reset();
44948         });
44949        
44950         
44951         return this;
44952     },
44953
44954     /**
44955      * Add Roo.form components to this form.
44956      * @param {Field} field1
44957      * @param {Field} field2 (optional)
44958      * @param {Field} etc (optional)
44959      * @return {BasicForm} this
44960      */
44961     add : function(){
44962         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44963         return this;
44964     },
44965
44966
44967     /**
44968      * Removes a field from the items collection (does NOT remove its markup).
44969      * @param {Field} field
44970      * @return {BasicForm} this
44971      */
44972     remove : function(field){
44973         this.items.remove(field);
44974         return this;
44975     },
44976
44977     /**
44978      * Looks at the fields in this form, checks them for an id attribute,
44979      * and calls applyTo on the existing dom element with that id.
44980      * @return {BasicForm} this
44981      */
44982     render : function(){
44983         this.items.each(function(f){
44984             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
44985                 f.applyTo(f.id);
44986             }
44987         });
44988         return this;
44989     },
44990
44991     /**
44992      * Calls {@link Ext#apply} for all fields in this form with the passed object.
44993      * @param {Object} values
44994      * @return {BasicForm} this
44995      */
44996     applyToFields : function(o){
44997         this.items.each(function(f){
44998            Roo.apply(f, o);
44999         });
45000         return this;
45001     },
45002
45003     /**
45004      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45005      * @param {Object} values
45006      * @return {BasicForm} this
45007      */
45008     applyIfToFields : function(o){
45009         this.items.each(function(f){
45010            Roo.applyIf(f, o);
45011         });
45012         return this;
45013     }
45014 });
45015
45016 // back compat
45017 Roo.BasicForm = Roo.form.BasicForm;/*
45018  * Based on:
45019  * Ext JS Library 1.1.1
45020  * Copyright(c) 2006-2007, Ext JS, LLC.
45021  *
45022  * Originally Released Under LGPL - original licence link has changed is not relivant.
45023  *
45024  * Fork - LGPL
45025  * <script type="text/javascript">
45026  */
45027
45028 /**
45029  * @class Roo.form.Form
45030  * @extends Roo.form.BasicForm
45031  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45032  * @constructor
45033  * @param {Object} config Configuration options
45034  */
45035 Roo.form.Form = function(config){
45036     var xitems =  [];
45037     if (config.items) {
45038         xitems = config.items;
45039         delete config.items;
45040     }
45041    
45042     
45043     Roo.form.Form.superclass.constructor.call(this, null, config);
45044     this.url = this.url || this.action;
45045     if(!this.root){
45046         this.root = new Roo.form.Layout(Roo.applyIf({
45047             id: Roo.id()
45048         }, config));
45049     }
45050     this.active = this.root;
45051     /**
45052      * Array of all the buttons that have been added to this form via {@link addButton}
45053      * @type Array
45054      */
45055     this.buttons = [];
45056     this.allItems = [];
45057     this.addEvents({
45058         /**
45059          * @event clientvalidation
45060          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45061          * @param {Form} this
45062          * @param {Boolean} valid true if the form has passed client-side validation
45063          */
45064         clientvalidation: true,
45065         /**
45066          * @event rendered
45067          * Fires when the form is rendered
45068          * @param {Roo.form.Form} form
45069          */
45070         rendered : true
45071     });
45072     
45073     if (this.progressUrl) {
45074             // push a hidden field onto the list of fields..
45075             this.addxtype( {
45076                     xns: Roo.form, 
45077                     xtype : 'Hidden', 
45078                     name : 'UPLOAD_IDENTIFIER' 
45079             });
45080         }
45081         
45082     
45083     Roo.each(xitems, this.addxtype, this);
45084     
45085     
45086     
45087 };
45088
45089 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45090     /**
45091      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45092      */
45093     /**
45094      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45095      */
45096     /**
45097      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45098      */
45099     buttonAlign:'center',
45100
45101     /**
45102      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45103      */
45104     minButtonWidth:75,
45105
45106     /**
45107      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45108      * This property cascades to child containers if not set.
45109      */
45110     labelAlign:'left',
45111
45112     /**
45113      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45114      * fires a looping event with that state. This is required to bind buttons to the valid
45115      * state using the config value formBind:true on the button.
45116      */
45117     monitorValid : false,
45118
45119     /**
45120      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45121      */
45122     monitorPoll : 200,
45123     
45124     /**
45125      * @cfg {String} progressUrl - Url to return progress data 
45126      */
45127     
45128     progressUrl : false,
45129   
45130     /**
45131      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45132      * fields are added and the column is closed. If no fields are passed the column remains open
45133      * until end() is called.
45134      * @param {Object} config The config to pass to the column
45135      * @param {Field} field1 (optional)
45136      * @param {Field} field2 (optional)
45137      * @param {Field} etc (optional)
45138      * @return Column The column container object
45139      */
45140     column : function(c){
45141         var col = new Roo.form.Column(c);
45142         this.start(col);
45143         if(arguments.length > 1){ // duplicate code required because of Opera
45144             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45145             this.end();
45146         }
45147         return col;
45148     },
45149
45150     /**
45151      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45152      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45153      * until end() is called.
45154      * @param {Object} config The config to pass to the fieldset
45155      * @param {Field} field1 (optional)
45156      * @param {Field} field2 (optional)
45157      * @param {Field} etc (optional)
45158      * @return FieldSet The fieldset container object
45159      */
45160     fieldset : function(c){
45161         var fs = new Roo.form.FieldSet(c);
45162         this.start(fs);
45163         if(arguments.length > 1){ // duplicate code required because of Opera
45164             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45165             this.end();
45166         }
45167         return fs;
45168     },
45169
45170     /**
45171      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45172      * fields are added and the container is closed. If no fields are passed the container remains open
45173      * until end() is called.
45174      * @param {Object} config The config to pass to the Layout
45175      * @param {Field} field1 (optional)
45176      * @param {Field} field2 (optional)
45177      * @param {Field} etc (optional)
45178      * @return Layout The container object
45179      */
45180     container : function(c){
45181         var l = new Roo.form.Layout(c);
45182         this.start(l);
45183         if(arguments.length > 1){ // duplicate code required because of Opera
45184             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45185             this.end();
45186         }
45187         return l;
45188     },
45189
45190     /**
45191      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45192      * @param {Object} container A Roo.form.Layout or subclass of Layout
45193      * @return {Form} this
45194      */
45195     start : function(c){
45196         // cascade label info
45197         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45198         this.active.stack.push(c);
45199         c.ownerCt = this.active;
45200         this.active = c;
45201         return this;
45202     },
45203
45204     /**
45205      * Closes the current open container
45206      * @return {Form} this
45207      */
45208     end : function(){
45209         if(this.active == this.root){
45210             return this;
45211         }
45212         this.active = this.active.ownerCt;
45213         return this;
45214     },
45215
45216     /**
45217      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45218      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45219      * as the label of the field.
45220      * @param {Field} field1
45221      * @param {Field} field2 (optional)
45222      * @param {Field} etc. (optional)
45223      * @return {Form} this
45224      */
45225     add : function(){
45226         this.active.stack.push.apply(this.active.stack, arguments);
45227         this.allItems.push.apply(this.allItems,arguments);
45228         var r = [];
45229         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45230             if(a[i].isFormField){
45231                 r.push(a[i]);
45232             }
45233         }
45234         if(r.length > 0){
45235             Roo.form.Form.superclass.add.apply(this, r);
45236         }
45237         return this;
45238     },
45239     
45240
45241     
45242     
45243     
45244      /**
45245      * Find any element that has been added to a form, using it's ID or name
45246      * This can include framesets, columns etc. along with regular fields..
45247      * @param {String} id - id or name to find.
45248      
45249      * @return {Element} e - or false if nothing found.
45250      */
45251     findbyId : function(id)
45252     {
45253         var ret = false;
45254         if (!id) {
45255             return ret;
45256         }
45257         Roo.each(this.allItems, function(f){
45258             if (f.id == id || f.name == id ){
45259                 ret = f;
45260                 return false;
45261             }
45262         });
45263         return ret;
45264     },
45265
45266     
45267     
45268     /**
45269      * Render this form into the passed container. This should only be called once!
45270      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45271      * @return {Form} this
45272      */
45273     render : function(ct)
45274     {
45275         
45276         
45277         
45278         ct = Roo.get(ct);
45279         var o = this.autoCreate || {
45280             tag: 'form',
45281             method : this.method || 'POST',
45282             id : this.id || Roo.id()
45283         };
45284         this.initEl(ct.createChild(o));
45285
45286         this.root.render(this.el);
45287         
45288        
45289              
45290         this.items.each(function(f){
45291             f.render('x-form-el-'+f.id);
45292         });
45293
45294         if(this.buttons.length > 0){
45295             // tables are required to maintain order and for correct IE layout
45296             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45297                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45298                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45299             }}, null, true);
45300             var tr = tb.getElementsByTagName('tr')[0];
45301             for(var i = 0, len = this.buttons.length; i < len; i++) {
45302                 var b = this.buttons[i];
45303                 var td = document.createElement('td');
45304                 td.className = 'x-form-btn-td';
45305                 b.render(tr.appendChild(td));
45306             }
45307         }
45308         if(this.monitorValid){ // initialize after render
45309             this.startMonitoring();
45310         }
45311         this.fireEvent('rendered', this);
45312         return this;
45313     },
45314
45315     /**
45316      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45317      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45318      * object or a valid Roo.DomHelper element config
45319      * @param {Function} handler The function called when the button is clicked
45320      * @param {Object} scope (optional) The scope of the handler function
45321      * @return {Roo.Button}
45322      */
45323     addButton : function(config, handler, scope){
45324         var bc = {
45325             handler: handler,
45326             scope: scope,
45327             minWidth: this.minButtonWidth,
45328             hideParent:true
45329         };
45330         if(typeof config == "string"){
45331             bc.text = config;
45332         }else{
45333             Roo.apply(bc, config);
45334         }
45335         var btn = new Roo.Button(null, bc);
45336         this.buttons.push(btn);
45337         return btn;
45338     },
45339
45340      /**
45341      * Adds a series of form elements (using the xtype property as the factory method.
45342      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45343      * @param {Object} config 
45344      */
45345     
45346     addxtype : function()
45347     {
45348         var ar = Array.prototype.slice.call(arguments, 0);
45349         var ret = false;
45350         for(var i = 0; i < ar.length; i++) {
45351             if (!ar[i]) {
45352                 continue; // skip -- if this happends something invalid got sent, we 
45353                 // should ignore it, as basically that interface element will not show up
45354                 // and that should be pretty obvious!!
45355             }
45356             
45357             if (Roo.form[ar[i].xtype]) {
45358                 ar[i].form = this;
45359                 var fe = Roo.factory(ar[i], Roo.form);
45360                 if (!ret) {
45361                     ret = fe;
45362                 }
45363                 fe.form = this;
45364                 if (fe.store) {
45365                     fe.store.form = this;
45366                 }
45367                 if (fe.isLayout) {  
45368                          
45369                     this.start(fe);
45370                     this.allItems.push(fe);
45371                     if (fe.items && fe.addxtype) {
45372                         fe.addxtype.apply(fe, fe.items);
45373                         delete fe.items;
45374                     }
45375                      this.end();
45376                     continue;
45377                 }
45378                 
45379                 
45380                  
45381                 this.add(fe);
45382               //  console.log('adding ' + ar[i].xtype);
45383             }
45384             if (ar[i].xtype == 'Button') {  
45385                 //console.log('adding button');
45386                 //console.log(ar[i]);
45387                 this.addButton(ar[i]);
45388                 this.allItems.push(fe);
45389                 continue;
45390             }
45391             
45392             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45393                 alert('end is not supported on xtype any more, use items');
45394             //    this.end();
45395             //    //console.log('adding end');
45396             }
45397             
45398         }
45399         return ret;
45400     },
45401     
45402     /**
45403      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45404      * option "monitorValid"
45405      */
45406     startMonitoring : function(){
45407         if(!this.bound){
45408             this.bound = true;
45409             Roo.TaskMgr.start({
45410                 run : this.bindHandler,
45411                 interval : this.monitorPoll || 200,
45412                 scope: this
45413             });
45414         }
45415     },
45416
45417     /**
45418      * Stops monitoring of the valid state of this form
45419      */
45420     stopMonitoring : function(){
45421         this.bound = false;
45422     },
45423
45424     // private
45425     bindHandler : function(){
45426         if(!this.bound){
45427             return false; // stops binding
45428         }
45429         var valid = true;
45430         this.items.each(function(f){
45431             if(!f.isValid(true)){
45432                 valid = false;
45433                 return false;
45434             }
45435         });
45436         for(var i = 0, len = this.buttons.length; i < len; i++){
45437             var btn = this.buttons[i];
45438             if(btn.formBind === true && btn.disabled === valid){
45439                 btn.setDisabled(!valid);
45440             }
45441         }
45442         this.fireEvent('clientvalidation', this, valid);
45443     }
45444     
45445     
45446     
45447     
45448     
45449     
45450     
45451     
45452 });
45453
45454
45455 // back compat
45456 Roo.Form = Roo.form.Form;
45457 /*
45458  * Based on:
45459  * Ext JS Library 1.1.1
45460  * Copyright(c) 2006-2007, Ext JS, LLC.
45461  *
45462  * Originally Released Under LGPL - original licence link has changed is not relivant.
45463  *
45464  * Fork - LGPL
45465  * <script type="text/javascript">
45466  */
45467
45468 // as we use this in bootstrap.
45469 Roo.namespace('Roo.form');
45470  /**
45471  * @class Roo.form.Action
45472  * Internal Class used to handle form actions
45473  * @constructor
45474  * @param {Roo.form.BasicForm} el The form element or its id
45475  * @param {Object} config Configuration options
45476  */
45477
45478  
45479  
45480 // define the action interface
45481 Roo.form.Action = function(form, options){
45482     this.form = form;
45483     this.options = options || {};
45484 };
45485 /**
45486  * Client Validation Failed
45487  * @const 
45488  */
45489 Roo.form.Action.CLIENT_INVALID = 'client';
45490 /**
45491  * Server Validation Failed
45492  * @const 
45493  */
45494 Roo.form.Action.SERVER_INVALID = 'server';
45495  /**
45496  * Connect to Server Failed
45497  * @const 
45498  */
45499 Roo.form.Action.CONNECT_FAILURE = 'connect';
45500 /**
45501  * Reading Data from Server Failed
45502  * @const 
45503  */
45504 Roo.form.Action.LOAD_FAILURE = 'load';
45505
45506 Roo.form.Action.prototype = {
45507     type : 'default',
45508     failureType : undefined,
45509     response : undefined,
45510     result : undefined,
45511
45512     // interface method
45513     run : function(options){
45514
45515     },
45516
45517     // interface method
45518     success : function(response){
45519
45520     },
45521
45522     // interface method
45523     handleResponse : function(response){
45524
45525     },
45526
45527     // default connection failure
45528     failure : function(response){
45529         
45530         this.response = response;
45531         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45532         this.form.afterAction(this, false);
45533     },
45534
45535     processResponse : function(response){
45536         this.response = response;
45537         if(!response.responseText){
45538             return true;
45539         }
45540         this.result = this.handleResponse(response);
45541         return this.result;
45542     },
45543
45544     // utility functions used internally
45545     getUrl : function(appendParams){
45546         var url = this.options.url || this.form.url || this.form.el.dom.action;
45547         if(appendParams){
45548             var p = this.getParams();
45549             if(p){
45550                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
45551             }
45552         }
45553         return url;
45554     },
45555
45556     getMethod : function(){
45557         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
45558     },
45559
45560     getParams : function(){
45561         var bp = this.form.baseParams;
45562         var p = this.options.params;
45563         if(p){
45564             if(typeof p == "object"){
45565                 p = Roo.urlEncode(Roo.applyIf(p, bp));
45566             }else if(typeof p == 'string' && bp){
45567                 p += '&' + Roo.urlEncode(bp);
45568             }
45569         }else if(bp){
45570             p = Roo.urlEncode(bp);
45571         }
45572         return p;
45573     },
45574
45575     createCallback : function(){
45576         return {
45577             success: this.success,
45578             failure: this.failure,
45579             scope: this,
45580             timeout: (this.form.timeout*1000),
45581             upload: this.form.fileUpload ? this.success : undefined
45582         };
45583     }
45584 };
45585
45586 Roo.form.Action.Submit = function(form, options){
45587     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
45588 };
45589
45590 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
45591     type : 'submit',
45592
45593     haveProgress : false,
45594     uploadComplete : false,
45595     
45596     // uploadProgress indicator.
45597     uploadProgress : function()
45598     {
45599         if (!this.form.progressUrl) {
45600             return;
45601         }
45602         
45603         if (!this.haveProgress) {
45604             Roo.MessageBox.progress("Uploading", "Uploading");
45605         }
45606         if (this.uploadComplete) {
45607            Roo.MessageBox.hide();
45608            return;
45609         }
45610         
45611         this.haveProgress = true;
45612    
45613         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
45614         
45615         var c = new Roo.data.Connection();
45616         c.request({
45617             url : this.form.progressUrl,
45618             params: {
45619                 id : uid
45620             },
45621             method: 'GET',
45622             success : function(req){
45623                //console.log(data);
45624                 var rdata = false;
45625                 var edata;
45626                 try  {
45627                    rdata = Roo.decode(req.responseText)
45628                 } catch (e) {
45629                     Roo.log("Invalid data from server..");
45630                     Roo.log(edata);
45631                     return;
45632                 }
45633                 if (!rdata || !rdata.success) {
45634                     Roo.log(rdata);
45635                     Roo.MessageBox.alert(Roo.encode(rdata));
45636                     return;
45637                 }
45638                 var data = rdata.data;
45639                 
45640                 if (this.uploadComplete) {
45641                    Roo.MessageBox.hide();
45642                    return;
45643                 }
45644                    
45645                 if (data){
45646                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
45647                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
45648                     );
45649                 }
45650                 this.uploadProgress.defer(2000,this);
45651             },
45652        
45653             failure: function(data) {
45654                 Roo.log('progress url failed ');
45655                 Roo.log(data);
45656             },
45657             scope : this
45658         });
45659            
45660     },
45661     
45662     
45663     run : function()
45664     {
45665         // run get Values on the form, so it syncs any secondary forms.
45666         this.form.getValues();
45667         
45668         var o = this.options;
45669         var method = this.getMethod();
45670         var isPost = method == 'POST';
45671         if(o.clientValidation === false || this.form.isValid()){
45672             
45673             if (this.form.progressUrl) {
45674                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
45675                     (new Date() * 1) + '' + Math.random());
45676                     
45677             } 
45678             
45679             
45680             Roo.Ajax.request(Roo.apply(this.createCallback(), {
45681                 form:this.form.el.dom,
45682                 url:this.getUrl(!isPost),
45683                 method: method,
45684                 params:isPost ? this.getParams() : null,
45685                 isUpload: this.form.fileUpload
45686             }));
45687             
45688             this.uploadProgress();
45689
45690         }else if (o.clientValidation !== false){ // client validation failed
45691             this.failureType = Roo.form.Action.CLIENT_INVALID;
45692             this.form.afterAction(this, false);
45693         }
45694     },
45695
45696     success : function(response)
45697     {
45698         this.uploadComplete= true;
45699         if (this.haveProgress) {
45700             Roo.MessageBox.hide();
45701         }
45702         
45703         
45704         var result = this.processResponse(response);
45705         if(result === true || result.success){
45706             this.form.afterAction(this, true);
45707             return;
45708         }
45709         if(result.errors){
45710             this.form.markInvalid(result.errors);
45711             this.failureType = Roo.form.Action.SERVER_INVALID;
45712         }
45713         this.form.afterAction(this, false);
45714     },
45715     failure : function(response)
45716     {
45717         this.uploadComplete= true;
45718         if (this.haveProgress) {
45719             Roo.MessageBox.hide();
45720         }
45721         
45722         this.response = response;
45723         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45724         this.form.afterAction(this, false);
45725     },
45726     
45727     handleResponse : function(response){
45728         if(this.form.errorReader){
45729             var rs = this.form.errorReader.read(response);
45730             var errors = [];
45731             if(rs.records){
45732                 for(var i = 0, len = rs.records.length; i < len; i++) {
45733                     var r = rs.records[i];
45734                     errors[i] = r.data;
45735                 }
45736             }
45737             if(errors.length < 1){
45738                 errors = null;
45739             }
45740             return {
45741                 success : rs.success,
45742                 errors : errors
45743             };
45744         }
45745         var ret = false;
45746         try {
45747             ret = Roo.decode(response.responseText);
45748         } catch (e) {
45749             ret = {
45750                 success: false,
45751                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45752                 errors : []
45753             };
45754         }
45755         return ret;
45756         
45757     }
45758 });
45759
45760
45761 Roo.form.Action.Load = function(form, options){
45762     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45763     this.reader = this.form.reader;
45764 };
45765
45766 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45767     type : 'load',
45768
45769     run : function(){
45770         
45771         Roo.Ajax.request(Roo.apply(
45772                 this.createCallback(), {
45773                     method:this.getMethod(),
45774                     url:this.getUrl(false),
45775                     params:this.getParams()
45776         }));
45777     },
45778
45779     success : function(response){
45780         
45781         var result = this.processResponse(response);
45782         if(result === true || !result.success || !result.data){
45783             this.failureType = Roo.form.Action.LOAD_FAILURE;
45784             this.form.afterAction(this, false);
45785             return;
45786         }
45787         this.form.clearInvalid();
45788         this.form.setValues(result.data);
45789         this.form.afterAction(this, true);
45790     },
45791
45792     handleResponse : function(response){
45793         if(this.form.reader){
45794             var rs = this.form.reader.read(response);
45795             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45796             return {
45797                 success : rs.success,
45798                 data : data
45799             };
45800         }
45801         return Roo.decode(response.responseText);
45802     }
45803 });
45804
45805 Roo.form.Action.ACTION_TYPES = {
45806     'load' : Roo.form.Action.Load,
45807     'submit' : Roo.form.Action.Submit
45808 };/*
45809  * Based on:
45810  * Ext JS Library 1.1.1
45811  * Copyright(c) 2006-2007, Ext JS, LLC.
45812  *
45813  * Originally Released Under LGPL - original licence link has changed is not relivant.
45814  *
45815  * Fork - LGPL
45816  * <script type="text/javascript">
45817  */
45818  
45819 /**
45820  * @class Roo.form.Layout
45821  * @extends Roo.Component
45822  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45823  * @constructor
45824  * @param {Object} config Configuration options
45825  */
45826 Roo.form.Layout = function(config){
45827     var xitems = [];
45828     if (config.items) {
45829         xitems = config.items;
45830         delete config.items;
45831     }
45832     Roo.form.Layout.superclass.constructor.call(this, config);
45833     this.stack = [];
45834     Roo.each(xitems, this.addxtype, this);
45835      
45836 };
45837
45838 Roo.extend(Roo.form.Layout, Roo.Component, {
45839     /**
45840      * @cfg {String/Object} autoCreate
45841      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45842      */
45843     /**
45844      * @cfg {String/Object/Function} style
45845      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45846      * a function which returns such a specification.
45847      */
45848     /**
45849      * @cfg {String} labelAlign
45850      * Valid values are "left," "top" and "right" (defaults to "left")
45851      */
45852     /**
45853      * @cfg {Number} labelWidth
45854      * Fixed width in pixels of all field labels (defaults to undefined)
45855      */
45856     /**
45857      * @cfg {Boolean} clear
45858      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45859      */
45860     clear : true,
45861     /**
45862      * @cfg {String} labelSeparator
45863      * The separator to use after field labels (defaults to ':')
45864      */
45865     labelSeparator : ':',
45866     /**
45867      * @cfg {Boolean} hideLabels
45868      * True to suppress the display of field labels in this layout (defaults to false)
45869      */
45870     hideLabels : false,
45871
45872     // private
45873     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45874     
45875     isLayout : true,
45876     
45877     // private
45878     onRender : function(ct, position){
45879         if(this.el){ // from markup
45880             this.el = Roo.get(this.el);
45881         }else {  // generate
45882             var cfg = this.getAutoCreate();
45883             this.el = ct.createChild(cfg, position);
45884         }
45885         if(this.style){
45886             this.el.applyStyles(this.style);
45887         }
45888         if(this.labelAlign){
45889             this.el.addClass('x-form-label-'+this.labelAlign);
45890         }
45891         if(this.hideLabels){
45892             this.labelStyle = "display:none";
45893             this.elementStyle = "padding-left:0;";
45894         }else{
45895             if(typeof this.labelWidth == 'number'){
45896                 this.labelStyle = "width:"+this.labelWidth+"px;";
45897                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45898             }
45899             if(this.labelAlign == 'top'){
45900                 this.labelStyle = "width:auto;";
45901                 this.elementStyle = "padding-left:0;";
45902             }
45903         }
45904         var stack = this.stack;
45905         var slen = stack.length;
45906         if(slen > 0){
45907             if(!this.fieldTpl){
45908                 var t = new Roo.Template(
45909                     '<div class="x-form-item {5}">',
45910                         '<label for="{0}" style="{2}">{1}{4}</label>',
45911                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45912                         '</div>',
45913                     '</div><div class="x-form-clear-left"></div>'
45914                 );
45915                 t.disableFormats = true;
45916                 t.compile();
45917                 Roo.form.Layout.prototype.fieldTpl = t;
45918             }
45919             for(var i = 0; i < slen; i++) {
45920                 if(stack[i].isFormField){
45921                     this.renderField(stack[i]);
45922                 }else{
45923                     this.renderComponent(stack[i]);
45924                 }
45925             }
45926         }
45927         if(this.clear){
45928             this.el.createChild({cls:'x-form-clear'});
45929         }
45930     },
45931
45932     // private
45933     renderField : function(f){
45934         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45935                f.id, //0
45936                f.fieldLabel, //1
45937                f.labelStyle||this.labelStyle||'', //2
45938                this.elementStyle||'', //3
45939                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45940                f.itemCls||this.itemCls||''  //5
45941        ], true).getPrevSibling());
45942     },
45943
45944     // private
45945     renderComponent : function(c){
45946         c.render(c.isLayout ? this.el : this.el.createChild());    
45947     },
45948     /**
45949      * Adds a object form elements (using the xtype property as the factory method.)
45950      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45951      * @param {Object} config 
45952      */
45953     addxtype : function(o)
45954     {
45955         // create the lement.
45956         o.form = this.form;
45957         var fe = Roo.factory(o, Roo.form);
45958         this.form.allItems.push(fe);
45959         this.stack.push(fe);
45960         
45961         if (fe.isFormField) {
45962             this.form.items.add(fe);
45963         }
45964          
45965         return fe;
45966     }
45967 });
45968
45969 /**
45970  * @class Roo.form.Column
45971  * @extends Roo.form.Layout
45972  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
45973  * @constructor
45974  * @param {Object} config Configuration options
45975  */
45976 Roo.form.Column = function(config){
45977     Roo.form.Column.superclass.constructor.call(this, config);
45978 };
45979
45980 Roo.extend(Roo.form.Column, Roo.form.Layout, {
45981     /**
45982      * @cfg {Number/String} width
45983      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45984      */
45985     /**
45986      * @cfg {String/Object} autoCreate
45987      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
45988      */
45989
45990     // private
45991     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
45992
45993     // private
45994     onRender : function(ct, position){
45995         Roo.form.Column.superclass.onRender.call(this, ct, position);
45996         if(this.width){
45997             this.el.setWidth(this.width);
45998         }
45999     }
46000 });
46001
46002
46003 /**
46004  * @class Roo.form.Row
46005  * @extends Roo.form.Layout
46006  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46007  * @constructor
46008  * @param {Object} config Configuration options
46009  */
46010
46011  
46012 Roo.form.Row = function(config){
46013     Roo.form.Row.superclass.constructor.call(this, config);
46014 };
46015  
46016 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46017       /**
46018      * @cfg {Number/String} width
46019      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46020      */
46021     /**
46022      * @cfg {Number/String} height
46023      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46024      */
46025     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46026     
46027     padWidth : 20,
46028     // private
46029     onRender : function(ct, position){
46030         //console.log('row render');
46031         if(!this.rowTpl){
46032             var t = new Roo.Template(
46033                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46034                     '<label for="{0}" style="{2}">{1}{4}</label>',
46035                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46036                     '</div>',
46037                 '</div>'
46038             );
46039             t.disableFormats = true;
46040             t.compile();
46041             Roo.form.Layout.prototype.rowTpl = t;
46042         }
46043         this.fieldTpl = this.rowTpl;
46044         
46045         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46046         var labelWidth = 100;
46047         
46048         if ((this.labelAlign != 'top')) {
46049             if (typeof this.labelWidth == 'number') {
46050                 labelWidth = this.labelWidth
46051             }
46052             this.padWidth =  20 + labelWidth;
46053             
46054         }
46055         
46056         Roo.form.Column.superclass.onRender.call(this, ct, position);
46057         if(this.width){
46058             this.el.setWidth(this.width);
46059         }
46060         if(this.height){
46061             this.el.setHeight(this.height);
46062         }
46063     },
46064     
46065     // private
46066     renderField : function(f){
46067         f.fieldEl = this.fieldTpl.append(this.el, [
46068                f.id, f.fieldLabel,
46069                f.labelStyle||this.labelStyle||'',
46070                this.elementStyle||'',
46071                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46072                f.itemCls||this.itemCls||'',
46073                f.width ? f.width + this.padWidth : 160 + this.padWidth
46074        ],true);
46075     }
46076 });
46077  
46078
46079 /**
46080  * @class Roo.form.FieldSet
46081  * @extends Roo.form.Layout
46082  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46083  * @constructor
46084  * @param {Object} config Configuration options
46085  */
46086 Roo.form.FieldSet = function(config){
46087     Roo.form.FieldSet.superclass.constructor.call(this, config);
46088 };
46089
46090 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46091     /**
46092      * @cfg {String} legend
46093      * The text to display as the legend for the FieldSet (defaults to '')
46094      */
46095     /**
46096      * @cfg {String/Object} autoCreate
46097      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46098      */
46099
46100     // private
46101     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46102
46103     // private
46104     onRender : function(ct, position){
46105         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46106         if(this.legend){
46107             this.setLegend(this.legend);
46108         }
46109     },
46110
46111     // private
46112     setLegend : function(text){
46113         if(this.rendered){
46114             this.el.child('legend').update(text);
46115         }
46116     }
46117 });/*
46118  * Based on:
46119  * Ext JS Library 1.1.1
46120  * Copyright(c) 2006-2007, Ext JS, LLC.
46121  *
46122  * Originally Released Under LGPL - original licence link has changed is not relivant.
46123  *
46124  * Fork - LGPL
46125  * <script type="text/javascript">
46126  */
46127 /**
46128  * @class Roo.form.VTypes
46129  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46130  * @singleton
46131  */
46132 Roo.form.VTypes = function(){
46133     // closure these in so they are only created once.
46134     var alpha = /^[a-zA-Z_]+$/;
46135     var alphanum = /^[a-zA-Z0-9_]+$/;
46136     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46137     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46138
46139     // All these messages and functions are configurable
46140     return {
46141         /**
46142          * The function used to validate email addresses
46143          * @param {String} value The email address
46144          */
46145         'email' : function(v){
46146             return email.test(v);
46147         },
46148         /**
46149          * The error text to display when the email validation function returns false
46150          * @type String
46151          */
46152         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46153         /**
46154          * The keystroke filter mask to be applied on email input
46155          * @type RegExp
46156          */
46157         'emailMask' : /[a-z0-9_\.\-@]/i,
46158
46159         /**
46160          * The function used to validate URLs
46161          * @param {String} value The URL
46162          */
46163         'url' : function(v){
46164             return url.test(v);
46165         },
46166         /**
46167          * The error text to display when the url validation function returns false
46168          * @type String
46169          */
46170         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46171         
46172         /**
46173          * The function used to validate alpha values
46174          * @param {String} value The value
46175          */
46176         'alpha' : function(v){
46177             return alpha.test(v);
46178         },
46179         /**
46180          * The error text to display when the alpha validation function returns false
46181          * @type String
46182          */
46183         'alphaText' : 'This field should only contain letters and _',
46184         /**
46185          * The keystroke filter mask to be applied on alpha input
46186          * @type RegExp
46187          */
46188         'alphaMask' : /[a-z_]/i,
46189
46190         /**
46191          * The function used to validate alphanumeric values
46192          * @param {String} value The value
46193          */
46194         'alphanum' : function(v){
46195             return alphanum.test(v);
46196         },
46197         /**
46198          * The error text to display when the alphanumeric validation function returns false
46199          * @type String
46200          */
46201         'alphanumText' : 'This field should only contain letters, numbers and _',
46202         /**
46203          * The keystroke filter mask to be applied on alphanumeric input
46204          * @type RegExp
46205          */
46206         'alphanumMask' : /[a-z0-9_]/i
46207     };
46208 }();//<script type="text/javascript">
46209
46210 /**
46211  * @class Roo.form.FCKeditor
46212  * @extends Roo.form.TextArea
46213  * Wrapper around the FCKEditor http://www.fckeditor.net
46214  * @constructor
46215  * Creates a new FCKeditor
46216  * @param {Object} config Configuration options
46217  */
46218 Roo.form.FCKeditor = function(config){
46219     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46220     this.addEvents({
46221          /**
46222          * @event editorinit
46223          * Fired when the editor is initialized - you can add extra handlers here..
46224          * @param {FCKeditor} this
46225          * @param {Object} the FCK object.
46226          */
46227         editorinit : true
46228     });
46229     
46230     
46231 };
46232 Roo.form.FCKeditor.editors = { };
46233 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46234 {
46235     //defaultAutoCreate : {
46236     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46237     //},
46238     // private
46239     /**
46240      * @cfg {Object} fck options - see fck manual for details.
46241      */
46242     fckconfig : false,
46243     
46244     /**
46245      * @cfg {Object} fck toolbar set (Basic or Default)
46246      */
46247     toolbarSet : 'Basic',
46248     /**
46249      * @cfg {Object} fck BasePath
46250      */ 
46251     basePath : '/fckeditor/',
46252     
46253     
46254     frame : false,
46255     
46256     value : '',
46257     
46258    
46259     onRender : function(ct, position)
46260     {
46261         if(!this.el){
46262             this.defaultAutoCreate = {
46263                 tag: "textarea",
46264                 style:"width:300px;height:60px;",
46265                 autocomplete: "off"
46266             };
46267         }
46268         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46269         /*
46270         if(this.grow){
46271             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46272             if(this.preventScrollbars){
46273                 this.el.setStyle("overflow", "hidden");
46274             }
46275             this.el.setHeight(this.growMin);
46276         }
46277         */
46278         //console.log('onrender' + this.getId() );
46279         Roo.form.FCKeditor.editors[this.getId()] = this;
46280          
46281
46282         this.replaceTextarea() ;
46283         
46284     },
46285     
46286     getEditor : function() {
46287         return this.fckEditor;
46288     },
46289     /**
46290      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46291      * @param {Mixed} value The value to set
46292      */
46293     
46294     
46295     setValue : function(value)
46296     {
46297         //console.log('setValue: ' + value);
46298         
46299         if(typeof(value) == 'undefined') { // not sure why this is happending...
46300             return;
46301         }
46302         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46303         
46304         //if(!this.el || !this.getEditor()) {
46305         //    this.value = value;
46306             //this.setValue.defer(100,this,[value]);    
46307         //    return;
46308         //} 
46309         
46310         if(!this.getEditor()) {
46311             return;
46312         }
46313         
46314         this.getEditor().SetData(value);
46315         
46316         //
46317
46318     },
46319
46320     /**
46321      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46322      * @return {Mixed} value The field value
46323      */
46324     getValue : function()
46325     {
46326         
46327         if (this.frame && this.frame.dom.style.display == 'none') {
46328             return Roo.form.FCKeditor.superclass.getValue.call(this);
46329         }
46330         
46331         if(!this.el || !this.getEditor()) {
46332            
46333            // this.getValue.defer(100,this); 
46334             return this.value;
46335         }
46336        
46337         
46338         var value=this.getEditor().GetData();
46339         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46340         return Roo.form.FCKeditor.superclass.getValue.call(this);
46341         
46342
46343     },
46344
46345     /**
46346      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46347      * @return {Mixed} value The field value
46348      */
46349     getRawValue : function()
46350     {
46351         if (this.frame && this.frame.dom.style.display == 'none') {
46352             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46353         }
46354         
46355         if(!this.el || !this.getEditor()) {
46356             //this.getRawValue.defer(100,this); 
46357             return this.value;
46358             return;
46359         }
46360         
46361         
46362         
46363         var value=this.getEditor().GetData();
46364         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46365         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46366          
46367     },
46368     
46369     setSize : function(w,h) {
46370         
46371         
46372         
46373         //if (this.frame && this.frame.dom.style.display == 'none') {
46374         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46375         //    return;
46376         //}
46377         //if(!this.el || !this.getEditor()) {
46378         //    this.setSize.defer(100,this, [w,h]); 
46379         //    return;
46380         //}
46381         
46382         
46383         
46384         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46385         
46386         this.frame.dom.setAttribute('width', w);
46387         this.frame.dom.setAttribute('height', h);
46388         this.frame.setSize(w,h);
46389         
46390     },
46391     
46392     toggleSourceEdit : function(value) {
46393         
46394       
46395          
46396         this.el.dom.style.display = value ? '' : 'none';
46397         this.frame.dom.style.display = value ?  'none' : '';
46398         
46399     },
46400     
46401     
46402     focus: function(tag)
46403     {
46404         if (this.frame.dom.style.display == 'none') {
46405             return Roo.form.FCKeditor.superclass.focus.call(this);
46406         }
46407         if(!this.el || !this.getEditor()) {
46408             this.focus.defer(100,this, [tag]); 
46409             return;
46410         }
46411         
46412         
46413         
46414         
46415         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46416         this.getEditor().Focus();
46417         if (tgs.length) {
46418             if (!this.getEditor().Selection.GetSelection()) {
46419                 this.focus.defer(100,this, [tag]); 
46420                 return;
46421             }
46422             
46423             
46424             var r = this.getEditor().EditorDocument.createRange();
46425             r.setStart(tgs[0],0);
46426             r.setEnd(tgs[0],0);
46427             this.getEditor().Selection.GetSelection().removeAllRanges();
46428             this.getEditor().Selection.GetSelection().addRange(r);
46429             this.getEditor().Focus();
46430         }
46431         
46432     },
46433     
46434     
46435     
46436     replaceTextarea : function()
46437     {
46438         if ( document.getElementById( this.getId() + '___Frame' ) )
46439             return ;
46440         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46441         //{
46442             // We must check the elements firstly using the Id and then the name.
46443         var oTextarea = document.getElementById( this.getId() );
46444         
46445         var colElementsByName = document.getElementsByName( this.getId() ) ;
46446          
46447         oTextarea.style.display = 'none' ;
46448
46449         if ( oTextarea.tabIndex ) {            
46450             this.TabIndex = oTextarea.tabIndex ;
46451         }
46452         
46453         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46454         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46455         this.frame = Roo.get(this.getId() + '___Frame')
46456     },
46457     
46458     _getConfigHtml : function()
46459     {
46460         var sConfig = '' ;
46461
46462         for ( var o in this.fckconfig ) {
46463             sConfig += sConfig.length > 0  ? '&amp;' : '';
46464             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46465         }
46466
46467         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46468     },
46469     
46470     
46471     _getIFrameHtml : function()
46472     {
46473         var sFile = 'fckeditor.html' ;
46474         /* no idea what this is about..
46475         try
46476         {
46477             if ( (/fcksource=true/i).test( window.top.location.search ) )
46478                 sFile = 'fckeditor.original.html' ;
46479         }
46480         catch (e) { 
46481         */
46482
46483         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46484         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46485         
46486         
46487         var html = '<iframe id="' + this.getId() +
46488             '___Frame" src="' + sLink +
46489             '" width="' + this.width +
46490             '" height="' + this.height + '"' +
46491             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46492             ' frameborder="0" scrolling="no"></iframe>' ;
46493
46494         return html ;
46495     },
46496     
46497     _insertHtmlBefore : function( html, element )
46498     {
46499         if ( element.insertAdjacentHTML )       {
46500             // IE
46501             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46502         } else { // Gecko
46503             var oRange = document.createRange() ;
46504             oRange.setStartBefore( element ) ;
46505             var oFragment = oRange.createContextualFragment( html );
46506             element.parentNode.insertBefore( oFragment, element ) ;
46507         }
46508     }
46509     
46510     
46511   
46512     
46513     
46514     
46515     
46516
46517 });
46518
46519 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46520
46521 function FCKeditor_OnComplete(editorInstance){
46522     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46523     f.fckEditor = editorInstance;
46524     //console.log("loaded");
46525     f.fireEvent('editorinit', f, editorInstance);
46526
46527   
46528
46529  
46530
46531
46532
46533
46534
46535
46536
46537
46538
46539
46540
46541
46542
46543
46544
46545 //<script type="text/javascript">
46546 /**
46547  * @class Roo.form.GridField
46548  * @extends Roo.form.Field
46549  * Embed a grid (or editable grid into a form)
46550  * STATUS ALPHA
46551  * 
46552  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
46553  * it needs 
46554  * xgrid.store = Roo.data.Store
46555  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
46556  * xgrid.store.reader = Roo.data.JsonReader 
46557  * 
46558  * 
46559  * @constructor
46560  * Creates a new GridField
46561  * @param {Object} config Configuration options
46562  */
46563 Roo.form.GridField = function(config){
46564     Roo.form.GridField.superclass.constructor.call(this, config);
46565      
46566 };
46567
46568 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
46569     /**
46570      * @cfg {Number} width  - used to restrict width of grid..
46571      */
46572     width : 100,
46573     /**
46574      * @cfg {Number} height - used to restrict height of grid..
46575      */
46576     height : 50,
46577      /**
46578      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
46579          * 
46580          *}
46581      */
46582     xgrid : false, 
46583     /**
46584      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46585      * {tag: "input", type: "checkbox", autocomplete: "off"})
46586      */
46587    // defaultAutoCreate : { tag: 'div' },
46588     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46589     /**
46590      * @cfg {String} addTitle Text to include for adding a title.
46591      */
46592     addTitle : false,
46593     //
46594     onResize : function(){
46595         Roo.form.Field.superclass.onResize.apply(this, arguments);
46596     },
46597
46598     initEvents : function(){
46599         // Roo.form.Checkbox.superclass.initEvents.call(this);
46600         // has no events...
46601        
46602     },
46603
46604
46605     getResizeEl : function(){
46606         return this.wrap;
46607     },
46608
46609     getPositionEl : function(){
46610         return this.wrap;
46611     },
46612
46613     // private
46614     onRender : function(ct, position){
46615         
46616         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
46617         var style = this.style;
46618         delete this.style;
46619         
46620         Roo.form.GridField.superclass.onRender.call(this, ct, position);
46621         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
46622         this.viewEl = this.wrap.createChild({ tag: 'div' });
46623         if (style) {
46624             this.viewEl.applyStyles(style);
46625         }
46626         if (this.width) {
46627             this.viewEl.setWidth(this.width);
46628         }
46629         if (this.height) {
46630             this.viewEl.setHeight(this.height);
46631         }
46632         //if(this.inputValue !== undefined){
46633         //this.setValue(this.value);
46634         
46635         
46636         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
46637         
46638         
46639         this.grid.render();
46640         this.grid.getDataSource().on('remove', this.refreshValue, this);
46641         this.grid.getDataSource().on('update', this.refreshValue, this);
46642         this.grid.on('afteredit', this.refreshValue, this);
46643  
46644     },
46645      
46646     
46647     /**
46648      * Sets the value of the item. 
46649      * @param {String} either an object  or a string..
46650      */
46651     setValue : function(v){
46652         //this.value = v;
46653         v = v || []; // empty set..
46654         // this does not seem smart - it really only affects memoryproxy grids..
46655         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
46656             var ds = this.grid.getDataSource();
46657             // assumes a json reader..
46658             var data = {}
46659             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
46660             ds.loadData( data);
46661         }
46662         // clear selection so it does not get stale.
46663         if (this.grid.sm) { 
46664             this.grid.sm.clearSelections();
46665         }
46666         
46667         Roo.form.GridField.superclass.setValue.call(this, v);
46668         this.refreshValue();
46669         // should load data in the grid really....
46670     },
46671     
46672     // private
46673     refreshValue: function() {
46674          var val = [];
46675         this.grid.getDataSource().each(function(r) {
46676             val.push(r.data);
46677         });
46678         this.el.dom.value = Roo.encode(val);
46679     }
46680     
46681      
46682     
46683     
46684 });/*
46685  * Based on:
46686  * Ext JS Library 1.1.1
46687  * Copyright(c) 2006-2007, Ext JS, LLC.
46688  *
46689  * Originally Released Under LGPL - original licence link has changed is not relivant.
46690  *
46691  * Fork - LGPL
46692  * <script type="text/javascript">
46693  */
46694 /**
46695  * @class Roo.form.DisplayField
46696  * @extends Roo.form.Field
46697  * A generic Field to display non-editable data.
46698  * @constructor
46699  * Creates a new Display Field item.
46700  * @param {Object} config Configuration options
46701  */
46702 Roo.form.DisplayField = function(config){
46703     Roo.form.DisplayField.superclass.constructor.call(this, config);
46704     
46705 };
46706
46707 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
46708     inputType:      'hidden',
46709     allowBlank:     true,
46710     readOnly:         true,
46711     
46712  
46713     /**
46714      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46715      */
46716     focusClass : undefined,
46717     /**
46718      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46719      */
46720     fieldClass: 'x-form-field',
46721     
46722      /**
46723      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
46724      */
46725     valueRenderer: undefined,
46726     
46727     width: 100,
46728     /**
46729      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46730      * {tag: "input", type: "checkbox", autocomplete: "off"})
46731      */
46732      
46733  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46734
46735     onResize : function(){
46736         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
46737         
46738     },
46739
46740     initEvents : function(){
46741         // Roo.form.Checkbox.superclass.initEvents.call(this);
46742         // has no events...
46743        
46744     },
46745
46746
46747     getResizeEl : function(){
46748         return this.wrap;
46749     },
46750
46751     getPositionEl : function(){
46752         return this.wrap;
46753     },
46754
46755     // private
46756     onRender : function(ct, position){
46757         
46758         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46759         //if(this.inputValue !== undefined){
46760         this.wrap = this.el.wrap();
46761         
46762         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46763         
46764         if (this.bodyStyle) {
46765             this.viewEl.applyStyles(this.bodyStyle);
46766         }
46767         //this.viewEl.setStyle('padding', '2px');
46768         
46769         this.setValue(this.value);
46770         
46771     },
46772 /*
46773     // private
46774     initValue : Roo.emptyFn,
46775
46776   */
46777
46778         // private
46779     onClick : function(){
46780         
46781     },
46782
46783     /**
46784      * Sets the checked state of the checkbox.
46785      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46786      */
46787     setValue : function(v){
46788         this.value = v;
46789         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46790         // this might be called before we have a dom element..
46791         if (!this.viewEl) {
46792             return;
46793         }
46794         this.viewEl.dom.innerHTML = html;
46795         Roo.form.DisplayField.superclass.setValue.call(this, v);
46796
46797     }
46798 });/*
46799  * 
46800  * Licence- LGPL
46801  * 
46802  */
46803
46804 /**
46805  * @class Roo.form.DayPicker
46806  * @extends Roo.form.Field
46807  * A Day picker show [M] [T] [W] ....
46808  * @constructor
46809  * Creates a new Day Picker
46810  * @param {Object} config Configuration options
46811  */
46812 Roo.form.DayPicker= function(config){
46813     Roo.form.DayPicker.superclass.constructor.call(this, config);
46814      
46815 };
46816
46817 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46818     /**
46819      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46820      */
46821     focusClass : undefined,
46822     /**
46823      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46824      */
46825     fieldClass: "x-form-field",
46826    
46827     /**
46828      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46829      * {tag: "input", type: "checkbox", autocomplete: "off"})
46830      */
46831     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46832     
46833    
46834     actionMode : 'viewEl', 
46835     //
46836     // private
46837  
46838     inputType : 'hidden',
46839     
46840      
46841     inputElement: false, // real input element?
46842     basedOn: false, // ????
46843     
46844     isFormField: true, // not sure where this is needed!!!!
46845
46846     onResize : function(){
46847         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46848         if(!this.boxLabel){
46849             this.el.alignTo(this.wrap, 'c-c');
46850         }
46851     },
46852
46853     initEvents : function(){
46854         Roo.form.Checkbox.superclass.initEvents.call(this);
46855         this.el.on("click", this.onClick,  this);
46856         this.el.on("change", this.onClick,  this);
46857     },
46858
46859
46860     getResizeEl : function(){
46861         return this.wrap;
46862     },
46863
46864     getPositionEl : function(){
46865         return this.wrap;
46866     },
46867
46868     
46869     // private
46870     onRender : function(ct, position){
46871         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46872        
46873         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46874         
46875         var r1 = '<table><tr>';
46876         var r2 = '<tr class="x-form-daypick-icons">';
46877         for (var i=0; i < 7; i++) {
46878             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46879             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46880         }
46881         
46882         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46883         viewEl.select('img').on('click', this.onClick, this);
46884         this.viewEl = viewEl;   
46885         
46886         
46887         // this will not work on Chrome!!!
46888         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46889         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46890         
46891         
46892           
46893
46894     },
46895
46896     // private
46897     initValue : Roo.emptyFn,
46898
46899     /**
46900      * Returns the checked state of the checkbox.
46901      * @return {Boolean} True if checked, else false
46902      */
46903     getValue : function(){
46904         return this.el.dom.value;
46905         
46906     },
46907
46908         // private
46909     onClick : function(e){ 
46910         //this.setChecked(!this.checked);
46911         Roo.get(e.target).toggleClass('x-menu-item-checked');
46912         this.refreshValue();
46913         //if(this.el.dom.checked != this.checked){
46914         //    this.setValue(this.el.dom.checked);
46915        // }
46916     },
46917     
46918     // private
46919     refreshValue : function()
46920     {
46921         var val = '';
46922         this.viewEl.select('img',true).each(function(e,i,n)  {
46923             val += e.is(".x-menu-item-checked") ? String(n) : '';
46924         });
46925         this.setValue(val, true);
46926     },
46927
46928     /**
46929      * Sets the checked state of the checkbox.
46930      * On is always based on a string comparison between inputValue and the param.
46931      * @param {Boolean/String} value - the value to set 
46932      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46933      */
46934     setValue : function(v,suppressEvent){
46935         if (!this.el.dom) {
46936             return;
46937         }
46938         var old = this.el.dom.value ;
46939         this.el.dom.value = v;
46940         if (suppressEvent) {
46941             return ;
46942         }
46943          
46944         // update display..
46945         this.viewEl.select('img',true).each(function(e,i,n)  {
46946             
46947             var on = e.is(".x-menu-item-checked");
46948             var newv = v.indexOf(String(n)) > -1;
46949             if (on != newv) {
46950                 e.toggleClass('x-menu-item-checked');
46951             }
46952             
46953         });
46954         
46955         
46956         this.fireEvent('change', this, v, old);
46957         
46958         
46959     },
46960    
46961     // handle setting of hidden value by some other method!!?!?
46962     setFromHidden: function()
46963     {
46964         if(!this.el){
46965             return;
46966         }
46967         //console.log("SET FROM HIDDEN");
46968         //alert('setFrom hidden');
46969         this.setValue(this.el.dom.value);
46970     },
46971     
46972     onDestroy : function()
46973     {
46974         if(this.viewEl){
46975             Roo.get(this.viewEl).remove();
46976         }
46977          
46978         Roo.form.DayPicker.superclass.onDestroy.call(this);
46979     }
46980
46981 });/*
46982  * RooJS Library 1.1.1
46983  * Copyright(c) 2008-2011  Alan Knowles
46984  *
46985  * License - LGPL
46986  */
46987  
46988
46989 /**
46990  * @class Roo.form.ComboCheck
46991  * @extends Roo.form.ComboBox
46992  * A combobox for multiple select items.
46993  *
46994  * FIXME - could do with a reset button..
46995  * 
46996  * @constructor
46997  * Create a new ComboCheck
46998  * @param {Object} config Configuration options
46999  */
47000 Roo.form.ComboCheck = function(config){
47001     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47002     // should verify some data...
47003     // like
47004     // hiddenName = required..
47005     // displayField = required
47006     // valudField == required
47007     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47008     var _t = this;
47009     Roo.each(req, function(e) {
47010         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47011             throw "Roo.form.ComboCheck : missing value for: " + e;
47012         }
47013     });
47014     
47015     
47016 };
47017
47018 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47019      
47020      
47021     editable : false,
47022      
47023     selectedClass: 'x-menu-item-checked', 
47024     
47025     // private
47026     onRender : function(ct, position){
47027         var _t = this;
47028         
47029         
47030         
47031         if(!this.tpl){
47032             var cls = 'x-combo-list';
47033
47034             
47035             this.tpl =  new Roo.Template({
47036                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47037                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47038                    '<span>{' + this.displayField + '}</span>' +
47039                     '</div>' 
47040                 
47041             });
47042         }
47043  
47044         
47045         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47046         this.view.singleSelect = false;
47047         this.view.multiSelect = true;
47048         this.view.toggleSelect = true;
47049         this.pageTb.add(new Roo.Toolbar.Fill(), {
47050             
47051             text: 'Done',
47052             handler: function()
47053             {
47054                 _t.collapse();
47055             }
47056         });
47057     },
47058     
47059     onViewOver : function(e, t){
47060         // do nothing...
47061         return;
47062         
47063     },
47064     
47065     onViewClick : function(doFocus,index){
47066         return;
47067         
47068     },
47069     select: function () {
47070         //Roo.log("SELECT CALLED");
47071     },
47072      
47073     selectByValue : function(xv, scrollIntoView){
47074         var ar = this.getValueArray();
47075         var sels = [];
47076         
47077         Roo.each(ar, function(v) {
47078             if(v === undefined || v === null){
47079                 return;
47080             }
47081             var r = this.findRecord(this.valueField, v);
47082             if(r){
47083                 sels.push(this.store.indexOf(r))
47084                 
47085             }
47086         },this);
47087         this.view.select(sels);
47088         return false;
47089     },
47090     
47091     
47092     
47093     onSelect : function(record, index){
47094        // Roo.log("onselect Called");
47095        // this is only called by the clear button now..
47096         this.view.clearSelections();
47097         this.setValue('[]');
47098         if (this.value != this.valueBefore) {
47099             this.fireEvent('change', this, this.value, this.valueBefore);
47100             this.valueBefore = this.value;
47101         }
47102     },
47103     getValueArray : function()
47104     {
47105         var ar = [] ;
47106         
47107         try {
47108             //Roo.log(this.value);
47109             if (typeof(this.value) == 'undefined') {
47110                 return [];
47111             }
47112             var ar = Roo.decode(this.value);
47113             return  ar instanceof Array ? ar : []; //?? valid?
47114             
47115         } catch(e) {
47116             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47117             return [];
47118         }
47119          
47120     },
47121     expand : function ()
47122     {
47123         
47124         Roo.form.ComboCheck.superclass.expand.call(this);
47125         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47126         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47127         
47128
47129     },
47130     
47131     collapse : function(){
47132         Roo.form.ComboCheck.superclass.collapse.call(this);
47133         var sl = this.view.getSelectedIndexes();
47134         var st = this.store;
47135         var nv = [];
47136         var tv = [];
47137         var r;
47138         Roo.each(sl, function(i) {
47139             r = st.getAt(i);
47140             nv.push(r.get(this.valueField));
47141         },this);
47142         this.setValue(Roo.encode(nv));
47143         if (this.value != this.valueBefore) {
47144
47145             this.fireEvent('change', this, this.value, this.valueBefore);
47146             this.valueBefore = this.value;
47147         }
47148         
47149     },
47150     
47151     setValue : function(v){
47152         // Roo.log(v);
47153         this.value = v;
47154         
47155         var vals = this.getValueArray();
47156         var tv = [];
47157         Roo.each(vals, function(k) {
47158             var r = this.findRecord(this.valueField, k);
47159             if(r){
47160                 tv.push(r.data[this.displayField]);
47161             }else if(this.valueNotFoundText !== undefined){
47162                 tv.push( this.valueNotFoundText );
47163             }
47164         },this);
47165        // Roo.log(tv);
47166         
47167         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47168         this.hiddenField.value = v;
47169         this.value = v;
47170     }
47171     
47172 });/*
47173  * Based on:
47174  * Ext JS Library 1.1.1
47175  * Copyright(c) 2006-2007, Ext JS, LLC.
47176  *
47177  * Originally Released Under LGPL - original licence link has changed is not relivant.
47178  *
47179  * Fork - LGPL
47180  * <script type="text/javascript">
47181  */
47182  
47183 /**
47184  * @class Roo.form.Signature
47185  * @extends Roo.form.Field
47186  * Signature field.  
47187  * @constructor
47188  * 
47189  * @param {Object} config Configuration options
47190  */
47191
47192 Roo.form.Signature = function(config){
47193     Roo.form.Signature.superclass.constructor.call(this, config);
47194     
47195     this.addEvents({// not in used??
47196          /**
47197          * @event confirm
47198          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47199              * @param {Roo.form.Signature} combo This combo box
47200              */
47201         'confirm' : true,
47202         /**
47203          * @event reset
47204          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47205              * @param {Roo.form.ComboBox} combo This combo box
47206              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47207              */
47208         'reset' : true
47209     });
47210 };
47211
47212 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47213     /**
47214      * @cfg {Object} labels Label to use when rendering a form.
47215      * defaults to 
47216      * labels : { 
47217      *      clear : "Clear",
47218      *      confirm : "Confirm"
47219      *  }
47220      */
47221     labels : { 
47222         clear : "Clear",
47223         confirm : "Confirm"
47224     },
47225     /**
47226      * @cfg {Number} width The signature panel width (defaults to 300)
47227      */
47228     width: 300,
47229     /**
47230      * @cfg {Number} height The signature panel height (defaults to 100)
47231      */
47232     height : 100,
47233     /**
47234      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47235      */
47236     allowBlank : false,
47237     
47238     //private
47239     // {Object} signPanel The signature SVG panel element (defaults to {})
47240     signPanel : {},
47241     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47242     isMouseDown : false,
47243     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47244     isConfirmed : false,
47245     // {String} signatureTmp SVG mapping string (defaults to empty string)
47246     signatureTmp : '',
47247     
47248     
47249     defaultAutoCreate : { // modified by initCompnoent..
47250         tag: "input",
47251         type:"hidden"
47252     },
47253
47254     // private
47255     onRender : function(ct, position){
47256         
47257         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47258         
47259         this.wrap = this.el.wrap({
47260             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47261         });
47262         
47263         this.createToolbar(this);
47264         this.signPanel = this.wrap.createChild({
47265                 tag: 'div',
47266                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47267             }, this.el
47268         );
47269             
47270         this.svgID = Roo.id();
47271         this.svgEl = this.signPanel.createChild({
47272               xmlns : 'http://www.w3.org/2000/svg',
47273               tag : 'svg',
47274               id : this.svgID + "-svg",
47275               width: this.width,
47276               height: this.height,
47277               viewBox: '0 0 '+this.width+' '+this.height,
47278               cn : [
47279                 {
47280                     tag: "rect",
47281                     id: this.svgID + "-svg-r",
47282                     width: this.width,
47283                     height: this.height,
47284                     fill: "#ffa"
47285                 },
47286                 {
47287                     tag: "line",
47288                     id: this.svgID + "-svg-l",
47289                     x1: "0", // start
47290                     y1: (this.height*0.8), // start set the line in 80% of height
47291                     x2: this.width, // end
47292                     y2: (this.height*0.8), // end set the line in 80% of height
47293                     'stroke': "#666",
47294                     'stroke-width': "1",
47295                     'stroke-dasharray': "3",
47296                     'shape-rendering': "crispEdges",
47297                     'pointer-events': "none"
47298                 },
47299                 {
47300                     tag: "path",
47301                     id: this.svgID + "-svg-p",
47302                     'stroke': "navy",
47303                     'stroke-width': "3",
47304                     'fill': "none",
47305                     'pointer-events': 'none'
47306                 }
47307               ]
47308         });
47309         this.createSVG();
47310         this.svgBox = this.svgEl.dom.getScreenCTM();
47311     },
47312     createSVG : function(){ 
47313         var svg = this.signPanel;
47314         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47315         var t = this;
47316
47317         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47318         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47319         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47320         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47321         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47322         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47323         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47324         
47325     },
47326     isTouchEvent : function(e){
47327         return e.type.match(/^touch/);
47328     },
47329     getCoords : function (e) {
47330         var pt    = this.svgEl.dom.createSVGPoint();
47331         pt.x = e.clientX; 
47332         pt.y = e.clientY;
47333         if (this.isTouchEvent(e)) {
47334             pt.x =  e.targetTouches[0].clientX 
47335             pt.y = e.targetTouches[0].clientY;
47336         }
47337         var a = this.svgEl.dom.getScreenCTM();
47338         var b = a.inverse();
47339         var mx = pt.matrixTransform(b);
47340         return mx.x + ',' + mx.y;
47341     },
47342     //mouse event headler 
47343     down : function (e) {
47344         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47345         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47346         
47347         this.isMouseDown = true;
47348         
47349         e.preventDefault();
47350     },
47351     move : function (e) {
47352         if (this.isMouseDown) {
47353             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47354             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47355         }
47356         
47357         e.preventDefault();
47358     },
47359     up : function (e) {
47360         this.isMouseDown = false;
47361         var sp = this.signatureTmp.split(' ');
47362         
47363         if(sp.length > 1){
47364             if(!sp[sp.length-2].match(/^L/)){
47365                 sp.pop();
47366                 sp.pop();
47367                 sp.push("");
47368                 this.signatureTmp = sp.join(" ");
47369             }
47370         }
47371         if(this.getValue() != this.signatureTmp){
47372             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47373             this.isConfirmed = false;
47374         }
47375         e.preventDefault();
47376     },
47377     
47378     /**
47379      * Protected method that will not generally be called directly. It
47380      * is called when the editor creates its toolbar. Override this method if you need to
47381      * add custom toolbar buttons.
47382      * @param {HtmlEditor} editor
47383      */
47384     createToolbar : function(editor){
47385          function btn(id, toggle, handler){
47386             var xid = fid + '-'+ id ;
47387             return {
47388                 id : xid,
47389                 cmd : id,
47390                 cls : 'x-btn-icon x-edit-'+id,
47391                 enableToggle:toggle !== false,
47392                 scope: editor, // was editor...
47393                 handler:handler||editor.relayBtnCmd,
47394                 clickEvent:'mousedown',
47395                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47396                 tabIndex:-1
47397             };
47398         }
47399         
47400         
47401         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47402         this.tb = tb;
47403         this.tb.add(
47404            {
47405                 cls : ' x-signature-btn x-signature-'+id,
47406                 scope: editor, // was editor...
47407                 handler: this.reset,
47408                 clickEvent:'mousedown',
47409                 text: this.labels.clear
47410             },
47411             {
47412                  xtype : 'Fill',
47413                  xns: Roo.Toolbar
47414             }, 
47415             {
47416                 cls : '  x-signature-btn x-signature-'+id,
47417                 scope: editor, // was editor...
47418                 handler: this.confirmHandler,
47419                 clickEvent:'mousedown',
47420                 text: this.labels.confirm
47421             }
47422         );
47423     
47424     },
47425     //public
47426     /**
47427      * when user is clicked confirm then show this image.....
47428      * 
47429      * @return {String} Image Data URI
47430      */
47431     getImageDataURI : function(){
47432         var svg = this.svgEl.dom.parentNode.innerHTML;
47433         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47434         return src; 
47435     },
47436     /**
47437      * 
47438      * @return {Boolean} this.isConfirmed
47439      */
47440     getConfirmed : function(){
47441         return this.isConfirmed;
47442     },
47443     /**
47444      * 
47445      * @return {Number} this.width
47446      */
47447     getWidth : function(){
47448         return this.width;
47449     },
47450     /**
47451      * 
47452      * @return {Number} this.height
47453      */
47454     getHeight : function(){
47455         return this.height;
47456     },
47457     // private
47458     getSignature : function(){
47459         return this.signatureTmp;
47460     },
47461     // private
47462     reset : function(){
47463         this.signatureTmp = '';
47464         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47465         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47466         this.isConfirmed = false;
47467         Roo.form.Signature.superclass.reset.call(this);
47468     },
47469     setSignature : function(s){
47470         this.signatureTmp = s;
47471         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47472         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47473         this.setValue(s);
47474         this.isConfirmed = false;
47475         Roo.form.Signature.superclass.reset.call(this);
47476     }, 
47477     test : function(){
47478 //        Roo.log(this.signPanel.dom.contentWindow.up())
47479     },
47480     //private
47481     setConfirmed : function(){
47482         
47483         
47484         
47485 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47486     },
47487     // private
47488     confirmHandler : function(){
47489         if(!this.getSignature()){
47490             return;
47491         }
47492         
47493         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47494         this.setValue(this.getSignature());
47495         this.isConfirmed = true;
47496         
47497         this.fireEvent('confirm', this);
47498     },
47499     // private
47500     // Subclasses should provide the validation implementation by overriding this
47501     validateValue : function(value){
47502         if(this.allowBlank){
47503             return true;
47504         }
47505         
47506         if(this.isConfirmed){
47507             return true;
47508         }
47509         return false;
47510     }
47511 });/*
47512  * Based on:
47513  * Ext JS Library 1.1.1
47514  * Copyright(c) 2006-2007, Ext JS, LLC.
47515  *
47516  * Originally Released Under LGPL - original licence link has changed is not relivant.
47517  *
47518  * Fork - LGPL
47519  * <script type="text/javascript">
47520  */
47521  
47522
47523 /**
47524  * @class Roo.form.ComboBox
47525  * @extends Roo.form.TriggerField
47526  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47527  * @constructor
47528  * Create a new ComboBox.
47529  * @param {Object} config Configuration options
47530  */
47531 Roo.form.Select = function(config){
47532     Roo.form.Select.superclass.constructor.call(this, config);
47533      
47534 };
47535
47536 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47537     /**
47538      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47539      */
47540     /**
47541      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47542      * rendering into an Roo.Editor, defaults to false)
47543      */
47544     /**
47545      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47546      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47547      */
47548     /**
47549      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
47550      */
47551     /**
47552      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
47553      * the dropdown list (defaults to undefined, with no header element)
47554      */
47555
47556      /**
47557      * @cfg {String/Roo.Template} tpl The template to use to render the output
47558      */
47559      
47560     // private
47561     defaultAutoCreate : {tag: "select"  },
47562     /**
47563      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
47564      */
47565     listWidth: undefined,
47566     /**
47567      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
47568      * mode = 'remote' or 'text' if mode = 'local')
47569      */
47570     displayField: undefined,
47571     /**
47572      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
47573      * mode = 'remote' or 'value' if mode = 'local'). 
47574      * Note: use of a valueField requires the user make a selection
47575      * in order for a value to be mapped.
47576      */
47577     valueField: undefined,
47578     
47579     
47580     /**
47581      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
47582      * field's data value (defaults to the underlying DOM element's name)
47583      */
47584     hiddenName: undefined,
47585     /**
47586      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
47587      */
47588     listClass: '',
47589     /**
47590      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
47591      */
47592     selectedClass: 'x-combo-selected',
47593     /**
47594      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
47595      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
47596      * which displays a downward arrow icon).
47597      */
47598     triggerClass : 'x-form-arrow-trigger',
47599     /**
47600      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
47601      */
47602     shadow:'sides',
47603     /**
47604      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
47605      * anchor positions (defaults to 'tl-bl')
47606      */
47607     listAlign: 'tl-bl?',
47608     /**
47609      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
47610      */
47611     maxHeight: 300,
47612     /**
47613      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
47614      * query specified by the allQuery config option (defaults to 'query')
47615      */
47616     triggerAction: 'query',
47617     /**
47618      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
47619      * (defaults to 4, does not apply if editable = false)
47620      */
47621     minChars : 4,
47622     /**
47623      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
47624      * delay (typeAheadDelay) if it matches a known value (defaults to false)
47625      */
47626     typeAhead: false,
47627     /**
47628      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
47629      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
47630      */
47631     queryDelay: 500,
47632     /**
47633      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
47634      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
47635      */
47636     pageSize: 0,
47637     /**
47638      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
47639      * when editable = true (defaults to false)
47640      */
47641     selectOnFocus:false,
47642     /**
47643      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
47644      */
47645     queryParam: 'query',
47646     /**
47647      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
47648      * when mode = 'remote' (defaults to 'Loading...')
47649      */
47650     loadingText: 'Loading...',
47651     /**
47652      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
47653      */
47654     resizable: false,
47655     /**
47656      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
47657      */
47658     handleHeight : 8,
47659     /**
47660      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
47661      * traditional select (defaults to true)
47662      */
47663     editable: true,
47664     /**
47665      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
47666      */
47667     allQuery: '',
47668     /**
47669      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
47670      */
47671     mode: 'remote',
47672     /**
47673      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
47674      * listWidth has a higher value)
47675      */
47676     minListWidth : 70,
47677     /**
47678      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
47679      * allow the user to set arbitrary text into the field (defaults to false)
47680      */
47681     forceSelection:false,
47682     /**
47683      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
47684      * if typeAhead = true (defaults to 250)
47685      */
47686     typeAheadDelay : 250,
47687     /**
47688      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
47689      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
47690      */
47691     valueNotFoundText : undefined,
47692     
47693     /**
47694      * @cfg {String} defaultValue The value displayed after loading the store.
47695      */
47696     defaultValue: '',
47697     
47698     /**
47699      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
47700      */
47701     blockFocus : false,
47702     
47703     /**
47704      * @cfg {Boolean} disableClear Disable showing of clear button.
47705      */
47706     disableClear : false,
47707     /**
47708      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
47709      */
47710     alwaysQuery : false,
47711     
47712     //private
47713     addicon : false,
47714     editicon: false,
47715     
47716     // element that contains real text value.. (when hidden is used..)
47717      
47718     // private
47719     onRender : function(ct, position){
47720         Roo.form.Field.prototype.onRender.call(this, ct, position);
47721         
47722         if(this.store){
47723             this.store.on('beforeload', this.onBeforeLoad, this);
47724             this.store.on('load', this.onLoad, this);
47725             this.store.on('loadexception', this.onLoadException, this);
47726             this.store.load({});
47727         }
47728         
47729         
47730         
47731     },
47732
47733     // private
47734     initEvents : function(){
47735         //Roo.form.ComboBox.superclass.initEvents.call(this);
47736  
47737     },
47738
47739     onDestroy : function(){
47740        
47741         if(this.store){
47742             this.store.un('beforeload', this.onBeforeLoad, this);
47743             this.store.un('load', this.onLoad, this);
47744             this.store.un('loadexception', this.onLoadException, this);
47745         }
47746         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47747     },
47748
47749     // private
47750     fireKey : function(e){
47751         if(e.isNavKeyPress() && !this.list.isVisible()){
47752             this.fireEvent("specialkey", this, e);
47753         }
47754     },
47755
47756     // private
47757     onResize: function(w, h){
47758         
47759         return; 
47760     
47761         
47762     },
47763
47764     /**
47765      * Allow or prevent the user from directly editing the field text.  If false is passed,
47766      * the user will only be able to select from the items defined in the dropdown list.  This method
47767      * is the runtime equivalent of setting the 'editable' config option at config time.
47768      * @param {Boolean} value True to allow the user to directly edit the field text
47769      */
47770     setEditable : function(value){
47771          
47772     },
47773
47774     // private
47775     onBeforeLoad : function(){
47776         
47777         Roo.log("Select before load");
47778         return;
47779     
47780         this.innerList.update(this.loadingText ?
47781                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47782         //this.restrictHeight();
47783         this.selectedIndex = -1;
47784     },
47785
47786     // private
47787     onLoad : function(){
47788
47789     
47790         var dom = this.el.dom;
47791         dom.innerHTML = '';
47792          var od = dom.ownerDocument;
47793          
47794         if (this.emptyText) {
47795             var op = od.createElement('option');
47796             op.setAttribute('value', '');
47797             op.innerHTML = String.format('{0}', this.emptyText);
47798             dom.appendChild(op);
47799         }
47800         if(this.store.getCount() > 0){
47801            
47802             var vf = this.valueField;
47803             var df = this.displayField;
47804             this.store.data.each(function(r) {
47805                 // which colmsn to use... testing - cdoe / title..
47806                 var op = od.createElement('option');
47807                 op.setAttribute('value', r.data[vf]);
47808                 op.innerHTML = String.format('{0}', r.data[df]);
47809                 dom.appendChild(op);
47810             });
47811             if (typeof(this.defaultValue != 'undefined')) {
47812                 this.setValue(this.defaultValue);
47813             }
47814             
47815              
47816         }else{
47817             //this.onEmptyResults();
47818         }
47819         //this.el.focus();
47820     },
47821     // private
47822     onLoadException : function()
47823     {
47824         dom.innerHTML = '';
47825             
47826         Roo.log("Select on load exception");
47827         return;
47828     
47829         this.collapse();
47830         Roo.log(this.store.reader.jsonData);
47831         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47832             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47833         }
47834         
47835         
47836     },
47837     // private
47838     onTypeAhead : function(){
47839          
47840     },
47841
47842     // private
47843     onSelect : function(record, index){
47844         Roo.log('on select?');
47845         return;
47846         if(this.fireEvent('beforeselect', this, record, index) !== false){
47847             this.setFromData(index > -1 ? record.data : false);
47848             this.collapse();
47849             this.fireEvent('select', this, record, index);
47850         }
47851     },
47852
47853     /**
47854      * Returns the currently selected field value or empty string if no value is set.
47855      * @return {String} value The selected value
47856      */
47857     getValue : function(){
47858         var dom = this.el.dom;
47859         this.value = dom.options[dom.selectedIndex].value;
47860         return this.value;
47861         
47862     },
47863
47864     /**
47865      * Clears any text/value currently set in the field
47866      */
47867     clearValue : function(){
47868         this.value = '';
47869         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47870         
47871     },
47872
47873     /**
47874      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47875      * will be displayed in the field.  If the value does not match the data value of an existing item,
47876      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47877      * Otherwise the field will be blank (although the value will still be set).
47878      * @param {String} value The value to match
47879      */
47880     setValue : function(v){
47881         var d = this.el.dom;
47882         for (var i =0; i < d.options.length;i++) {
47883             if (v == d.options[i].value) {
47884                 d.selectedIndex = i;
47885                 this.value = v;
47886                 return;
47887             }
47888         }
47889         this.clearValue();
47890     },
47891     /**
47892      * @property {Object} the last set data for the element
47893      */
47894     
47895     lastData : false,
47896     /**
47897      * Sets the value of the field based on a object which is related to the record format for the store.
47898      * @param {Object} value the value to set as. or false on reset?
47899      */
47900     setFromData : function(o){
47901         Roo.log('setfrom data?');
47902          
47903         
47904         
47905     },
47906     // private
47907     reset : function(){
47908         this.clearValue();
47909     },
47910     // private
47911     findRecord : function(prop, value){
47912         
47913         return false;
47914     
47915         var record;
47916         if(this.store.getCount() > 0){
47917             this.store.each(function(r){
47918                 if(r.data[prop] == value){
47919                     record = r;
47920                     return false;
47921                 }
47922                 return true;
47923             });
47924         }
47925         return record;
47926     },
47927     
47928     getName: function()
47929     {
47930         // returns hidden if it's set..
47931         if (!this.rendered) {return ''};
47932         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47933         
47934     },
47935      
47936
47937     
47938
47939     // private
47940     onEmptyResults : function(){
47941         Roo.log('empty results');
47942         //this.collapse();
47943     },
47944
47945     /**
47946      * Returns true if the dropdown list is expanded, else false.
47947      */
47948     isExpanded : function(){
47949         return false;
47950     },
47951
47952     /**
47953      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47954      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47955      * @param {String} value The data value of the item to select
47956      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47957      * selected item if it is not currently in view (defaults to true)
47958      * @return {Boolean} True if the value matched an item in the list, else false
47959      */
47960     selectByValue : function(v, scrollIntoView){
47961         Roo.log('select By Value');
47962         return false;
47963     
47964         if(v !== undefined && v !== null){
47965             var r = this.findRecord(this.valueField || this.displayField, v);
47966             if(r){
47967                 this.select(this.store.indexOf(r), scrollIntoView);
47968                 return true;
47969             }
47970         }
47971         return false;
47972     },
47973
47974     /**
47975      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
47976      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47977      * @param {Number} index The zero-based index of the list item to select
47978      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47979      * selected item if it is not currently in view (defaults to true)
47980      */
47981     select : function(index, scrollIntoView){
47982         Roo.log('select ');
47983         return  ;
47984         
47985         this.selectedIndex = index;
47986         this.view.select(index);
47987         if(scrollIntoView !== false){
47988             var el = this.view.getNode(index);
47989             if(el){
47990                 this.innerList.scrollChildIntoView(el, false);
47991             }
47992         }
47993     },
47994
47995       
47996
47997     // private
47998     validateBlur : function(){
47999         
48000         return;
48001         
48002     },
48003
48004     // private
48005     initQuery : function(){
48006         this.doQuery(this.getRawValue());
48007     },
48008
48009     // private
48010     doForce : function(){
48011         if(this.el.dom.value.length > 0){
48012             this.el.dom.value =
48013                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48014              
48015         }
48016     },
48017
48018     /**
48019      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48020      * query allowing the query action to be canceled if needed.
48021      * @param {String} query The SQL query to execute
48022      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48023      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48024      * saved in the current store (defaults to false)
48025      */
48026     doQuery : function(q, forceAll){
48027         
48028         Roo.log('doQuery?');
48029         if(q === undefined || q === null){
48030             q = '';
48031         }
48032         var qe = {
48033             query: q,
48034             forceAll: forceAll,
48035             combo: this,
48036             cancel:false
48037         };
48038         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48039             return false;
48040         }
48041         q = qe.query;
48042         forceAll = qe.forceAll;
48043         if(forceAll === true || (q.length >= this.minChars)){
48044             if(this.lastQuery != q || this.alwaysQuery){
48045                 this.lastQuery = q;
48046                 if(this.mode == 'local'){
48047                     this.selectedIndex = -1;
48048                     if(forceAll){
48049                         this.store.clearFilter();
48050                     }else{
48051                         this.store.filter(this.displayField, q);
48052                     }
48053                     this.onLoad();
48054                 }else{
48055                     this.store.baseParams[this.queryParam] = q;
48056                     this.store.load({
48057                         params: this.getParams(q)
48058                     });
48059                     this.expand();
48060                 }
48061             }else{
48062                 this.selectedIndex = -1;
48063                 this.onLoad();   
48064             }
48065         }
48066     },
48067
48068     // private
48069     getParams : function(q){
48070         var p = {};
48071         //p[this.queryParam] = q;
48072         if(this.pageSize){
48073             p.start = 0;
48074             p.limit = this.pageSize;
48075         }
48076         return p;
48077     },
48078
48079     /**
48080      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48081      */
48082     collapse : function(){
48083         
48084     },
48085
48086     // private
48087     collapseIf : function(e){
48088         
48089     },
48090
48091     /**
48092      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48093      */
48094     expand : function(){
48095         
48096     } ,
48097
48098     // private
48099      
48100
48101     /** 
48102     * @cfg {Boolean} grow 
48103     * @hide 
48104     */
48105     /** 
48106     * @cfg {Number} growMin 
48107     * @hide 
48108     */
48109     /** 
48110     * @cfg {Number} growMax 
48111     * @hide 
48112     */
48113     /**
48114      * @hide
48115      * @method autoSize
48116      */
48117     
48118     setWidth : function()
48119     {
48120         
48121     },
48122     getResizeEl : function(){
48123         return this.el;
48124     }
48125 });//<script type="text/javasscript">
48126  
48127
48128 /**
48129  * @class Roo.DDView
48130  * A DnD enabled version of Roo.View.
48131  * @param {Element/String} container The Element in which to create the View.
48132  * @param {String} tpl The template string used to create the markup for each element of the View
48133  * @param {Object} config The configuration properties. These include all the config options of
48134  * {@link Roo.View} plus some specific to this class.<br>
48135  * <p>
48136  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48137  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48138  * <p>
48139  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48140 .x-view-drag-insert-above {
48141         border-top:1px dotted #3366cc;
48142 }
48143 .x-view-drag-insert-below {
48144         border-bottom:1px dotted #3366cc;
48145 }
48146 </code></pre>
48147  * 
48148  */
48149  
48150 Roo.DDView = function(container, tpl, config) {
48151     Roo.DDView.superclass.constructor.apply(this, arguments);
48152     this.getEl().setStyle("outline", "0px none");
48153     this.getEl().unselectable();
48154     if (this.dragGroup) {
48155                 this.setDraggable(this.dragGroup.split(","));
48156     }
48157     if (this.dropGroup) {
48158                 this.setDroppable(this.dropGroup.split(","));
48159     }
48160     if (this.deletable) {
48161         this.setDeletable();
48162     }
48163     this.isDirtyFlag = false;
48164         this.addEvents({
48165                 "drop" : true
48166         });
48167 };
48168
48169 Roo.extend(Roo.DDView, Roo.View, {
48170 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48171 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48172 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48173 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48174
48175         isFormField: true,
48176
48177         reset: Roo.emptyFn,
48178         
48179         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48180
48181         validate: function() {
48182                 return true;
48183         },
48184         
48185         destroy: function() {
48186                 this.purgeListeners();
48187                 this.getEl.removeAllListeners();
48188                 this.getEl().remove();
48189                 if (this.dragZone) {
48190                         if (this.dragZone.destroy) {
48191                                 this.dragZone.destroy();
48192                         }
48193                 }
48194                 if (this.dropZone) {
48195                         if (this.dropZone.destroy) {
48196                                 this.dropZone.destroy();
48197                         }
48198                 }
48199         },
48200
48201 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48202         getName: function() {
48203                 return this.name;
48204         },
48205
48206 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48207         setValue: function(v) {
48208                 if (!this.store) {
48209                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48210                 }
48211                 var data = {};
48212                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48213                 this.store.proxy = new Roo.data.MemoryProxy(data);
48214                 this.store.load();
48215         },
48216
48217 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48218         getValue: function() {
48219                 var result = '(';
48220                 this.store.each(function(rec) {
48221                         result += rec.id + ',';
48222                 });
48223                 return result.substr(0, result.length - 1) + ')';
48224         },
48225         
48226         getIds: function() {
48227                 var i = 0, result = new Array(this.store.getCount());
48228                 this.store.each(function(rec) {
48229                         result[i++] = rec.id;
48230                 });
48231                 return result;
48232         },
48233         
48234         isDirty: function() {
48235                 return this.isDirtyFlag;
48236         },
48237
48238 /**
48239  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48240  *      whole Element becomes the target, and this causes the drop gesture to append.
48241  */
48242     getTargetFromEvent : function(e) {
48243                 var target = e.getTarget();
48244                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48245                 target = target.parentNode;
48246                 }
48247                 if (!target) {
48248                         target = this.el.dom.lastChild || this.el.dom;
48249                 }
48250                 return target;
48251     },
48252
48253 /**
48254  *      Create the drag data which consists of an object which has the property "ddel" as
48255  *      the drag proxy element. 
48256  */
48257     getDragData : function(e) {
48258         var target = this.findItemFromChild(e.getTarget());
48259                 if(target) {
48260                         this.handleSelection(e);
48261                         var selNodes = this.getSelectedNodes();
48262             var dragData = {
48263                 source: this,
48264                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48265                 nodes: selNodes,
48266                 records: []
48267                         };
48268                         var selectedIndices = this.getSelectedIndexes();
48269                         for (var i = 0; i < selectedIndices.length; i++) {
48270                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48271                         }
48272                         if (selNodes.length == 1) {
48273                                 dragData.ddel = target.cloneNode(true); // the div element
48274                         } else {
48275                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48276                                 div.className = 'multi-proxy';
48277                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48278                                         div.appendChild(selNodes[i].cloneNode(true));
48279                                 }
48280                                 dragData.ddel = div;
48281                         }
48282             //console.log(dragData)
48283             //console.log(dragData.ddel.innerHTML)
48284                         return dragData;
48285                 }
48286         //console.log('nodragData')
48287                 return false;
48288     },
48289     
48290 /**     Specify to which ddGroup items in this DDView may be dragged. */
48291     setDraggable: function(ddGroup) {
48292         if (ddGroup instanceof Array) {
48293                 Roo.each(ddGroup, this.setDraggable, this);
48294                 return;
48295         }
48296         if (this.dragZone) {
48297                 this.dragZone.addToGroup(ddGroup);
48298         } else {
48299                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48300                                 containerScroll: true,
48301                                 ddGroup: ddGroup 
48302
48303                         });
48304 //                      Draggability implies selection. DragZone's mousedown selects the element.
48305                         if (!this.multiSelect) { this.singleSelect = true; }
48306
48307 //                      Wire the DragZone's handlers up to methods in *this*
48308                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48309                 }
48310     },
48311
48312 /**     Specify from which ddGroup this DDView accepts drops. */
48313     setDroppable: function(ddGroup) {
48314         if (ddGroup instanceof Array) {
48315                 Roo.each(ddGroup, this.setDroppable, this);
48316                 return;
48317         }
48318         if (this.dropZone) {
48319                 this.dropZone.addToGroup(ddGroup);
48320         } else {
48321                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48322                                 containerScroll: true,
48323                                 ddGroup: ddGroup
48324                         });
48325
48326 //                      Wire the DropZone's handlers up to methods in *this*
48327                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48328                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48329                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48330                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48331                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48332                 }
48333     },
48334
48335 /**     Decide whether to drop above or below a View node. */
48336     getDropPoint : function(e, n, dd){
48337         if (n == this.el.dom) { return "above"; }
48338                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48339                 var c = t + (b - t) / 2;
48340                 var y = Roo.lib.Event.getPageY(e);
48341                 if(y <= c) {
48342                         return "above";
48343                 }else{
48344                         return "below";
48345                 }
48346     },
48347
48348     onNodeEnter : function(n, dd, e, data){
48349                 return false;
48350     },
48351     
48352     onNodeOver : function(n, dd, e, data){
48353                 var pt = this.getDropPoint(e, n, dd);
48354                 // set the insert point style on the target node
48355                 var dragElClass = this.dropNotAllowed;
48356                 if (pt) {
48357                         var targetElClass;
48358                         if (pt == "above"){
48359                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48360                                 targetElClass = "x-view-drag-insert-above";
48361                         } else {
48362                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48363                                 targetElClass = "x-view-drag-insert-below";
48364                         }
48365                         if (this.lastInsertClass != targetElClass){
48366                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48367                                 this.lastInsertClass = targetElClass;
48368                         }
48369                 }
48370                 return dragElClass;
48371         },
48372
48373     onNodeOut : function(n, dd, e, data){
48374                 this.removeDropIndicators(n);
48375     },
48376
48377     onNodeDrop : function(n, dd, e, data){
48378         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48379                 return false;
48380         }
48381         var pt = this.getDropPoint(e, n, dd);
48382                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48383                 if (pt == "below") { insertAt++; }
48384                 for (var i = 0; i < data.records.length; i++) {
48385                         var r = data.records[i];
48386                         var dup = this.store.getById(r.id);
48387                         if (dup && (dd != this.dragZone)) {
48388                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48389                         } else {
48390                                 if (data.copy) {
48391                                         this.store.insert(insertAt++, r.copy());
48392                                 } else {
48393                                         data.source.isDirtyFlag = true;
48394                                         r.store.remove(r);
48395                                         this.store.insert(insertAt++, r);
48396                                 }
48397                                 this.isDirtyFlag = true;
48398                         }
48399                 }
48400                 this.dragZone.cachedTarget = null;
48401                 return true;
48402     },
48403
48404     removeDropIndicators : function(n){
48405                 if(n){
48406                         Roo.fly(n).removeClass([
48407                                 "x-view-drag-insert-above",
48408                                 "x-view-drag-insert-below"]);
48409                         this.lastInsertClass = "_noclass";
48410                 }
48411     },
48412
48413 /**
48414  *      Utility method. Add a delete option to the DDView's context menu.
48415  *      @param {String} imageUrl The URL of the "delete" icon image.
48416  */
48417         setDeletable: function(imageUrl) {
48418                 if (!this.singleSelect && !this.multiSelect) {
48419                         this.singleSelect = true;
48420                 }
48421                 var c = this.getContextMenu();
48422                 this.contextMenu.on("itemclick", function(item) {
48423                         switch (item.id) {
48424                                 case "delete":
48425                                         this.remove(this.getSelectedIndexes());
48426                                         break;
48427                         }
48428                 }, this);
48429                 this.contextMenu.add({
48430                         icon: imageUrl,
48431                         id: "delete",
48432                         text: 'Delete'
48433                 });
48434         },
48435         
48436 /**     Return the context menu for this DDView. */
48437         getContextMenu: function() {
48438                 if (!this.contextMenu) {
48439 //                      Create the View's context menu
48440                         this.contextMenu = new Roo.menu.Menu({
48441                                 id: this.id + "-contextmenu"
48442                         });
48443                         this.el.on("contextmenu", this.showContextMenu, this);
48444                 }
48445                 return this.contextMenu;
48446         },
48447         
48448         disableContextMenu: function() {
48449                 if (this.contextMenu) {
48450                         this.el.un("contextmenu", this.showContextMenu, this);
48451                 }
48452         },
48453
48454         showContextMenu: function(e, item) {
48455         item = this.findItemFromChild(e.getTarget());
48456                 if (item) {
48457                         e.stopEvent();
48458                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48459                         this.contextMenu.showAt(e.getXY());
48460             }
48461     },
48462
48463 /**
48464  *      Remove {@link Roo.data.Record}s at the specified indices.
48465  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48466  */
48467     remove: function(selectedIndices) {
48468                 selectedIndices = [].concat(selectedIndices);
48469                 for (var i = 0; i < selectedIndices.length; i++) {
48470                         var rec = this.store.getAt(selectedIndices[i]);
48471                         this.store.remove(rec);
48472                 }
48473     },
48474
48475 /**
48476  *      Double click fires the event, but also, if this is draggable, and there is only one other
48477  *      related DropZone, it transfers the selected node.
48478  */
48479     onDblClick : function(e){
48480         var item = this.findItemFromChild(e.getTarget());
48481         if(item){
48482             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48483                 return false;
48484             }
48485             if (this.dragGroup) {
48486                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48487                     while (targets.indexOf(this.dropZone) > -1) {
48488                             targets.remove(this.dropZone);
48489                                 }
48490                     if (targets.length == 1) {
48491                                         this.dragZone.cachedTarget = null;
48492                         var el = Roo.get(targets[0].getEl());
48493                         var box = el.getBox(true);
48494                         targets[0].onNodeDrop(el.dom, {
48495                                 target: el.dom,
48496                                 xy: [box.x, box.y + box.height - 1]
48497                         }, null, this.getDragData(e));
48498                     }
48499                 }
48500         }
48501     },
48502     
48503     handleSelection: function(e) {
48504                 this.dragZone.cachedTarget = null;
48505         var item = this.findItemFromChild(e.getTarget());
48506         if (!item) {
48507                 this.clearSelections(true);
48508                 return;
48509         }
48510                 if (item && (this.multiSelect || this.singleSelect)){
48511                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48512                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48513                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48514                                 this.unselect(item);
48515                         } else {
48516                                 this.select(item, this.multiSelect && e.ctrlKey);
48517                                 this.lastSelection = item;
48518                         }
48519                 }
48520     },
48521
48522     onItemClick : function(item, index, e){
48523                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48524                         return false;
48525                 }
48526                 return true;
48527     },
48528
48529     unselect : function(nodeInfo, suppressEvent){
48530                 var node = this.getNode(nodeInfo);
48531                 if(node && this.isSelected(node)){
48532                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48533                                 Roo.fly(node).removeClass(this.selectedClass);
48534                                 this.selections.remove(node);
48535                                 if(!suppressEvent){
48536                                         this.fireEvent("selectionchange", this, this.selections);
48537                                 }
48538                         }
48539                 }
48540     }
48541 });
48542 /*
48543  * Based on:
48544  * Ext JS Library 1.1.1
48545  * Copyright(c) 2006-2007, Ext JS, LLC.
48546  *
48547  * Originally Released Under LGPL - original licence link has changed is not relivant.
48548  *
48549  * Fork - LGPL
48550  * <script type="text/javascript">
48551  */
48552  
48553 /**
48554  * @class Roo.LayoutManager
48555  * @extends Roo.util.Observable
48556  * Base class for layout managers.
48557  */
48558 Roo.LayoutManager = function(container, config){
48559     Roo.LayoutManager.superclass.constructor.call(this);
48560     this.el = Roo.get(container);
48561     // ie scrollbar fix
48562     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
48563         document.body.scroll = "no";
48564     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
48565         this.el.position('relative');
48566     }
48567     this.id = this.el.id;
48568     this.el.addClass("x-layout-container");
48569     /** false to disable window resize monitoring @type Boolean */
48570     this.monitorWindowResize = true;
48571     this.regions = {};
48572     this.addEvents({
48573         /**
48574          * @event layout
48575          * Fires when a layout is performed. 
48576          * @param {Roo.LayoutManager} this
48577          */
48578         "layout" : true,
48579         /**
48580          * @event regionresized
48581          * Fires when the user resizes a region. 
48582          * @param {Roo.LayoutRegion} region The resized region
48583          * @param {Number} newSize The new size (width for east/west, height for north/south)
48584          */
48585         "regionresized" : true,
48586         /**
48587          * @event regioncollapsed
48588          * Fires when a region is collapsed. 
48589          * @param {Roo.LayoutRegion} region The collapsed region
48590          */
48591         "regioncollapsed" : true,
48592         /**
48593          * @event regionexpanded
48594          * Fires when a region is expanded.  
48595          * @param {Roo.LayoutRegion} region The expanded region
48596          */
48597         "regionexpanded" : true
48598     });
48599     this.updating = false;
48600     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48601 };
48602
48603 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
48604     /**
48605      * Returns true if this layout is currently being updated
48606      * @return {Boolean}
48607      */
48608     isUpdating : function(){
48609         return this.updating; 
48610     },
48611     
48612     /**
48613      * Suspend the LayoutManager from doing auto-layouts while
48614      * making multiple add or remove calls
48615      */
48616     beginUpdate : function(){
48617         this.updating = true;    
48618     },
48619     
48620     /**
48621      * Restore auto-layouts and optionally disable the manager from performing a layout
48622      * @param {Boolean} noLayout true to disable a layout update 
48623      */
48624     endUpdate : function(noLayout){
48625         this.updating = false;
48626         if(!noLayout){
48627             this.layout();
48628         }    
48629     },
48630     
48631     layout: function(){
48632         
48633     },
48634     
48635     onRegionResized : function(region, newSize){
48636         this.fireEvent("regionresized", region, newSize);
48637         this.layout();
48638     },
48639     
48640     onRegionCollapsed : function(region){
48641         this.fireEvent("regioncollapsed", region);
48642     },
48643     
48644     onRegionExpanded : function(region){
48645         this.fireEvent("regionexpanded", region);
48646     },
48647         
48648     /**
48649      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
48650      * performs box-model adjustments.
48651      * @return {Object} The size as an object {width: (the width), height: (the height)}
48652      */
48653     getViewSize : function(){
48654         var size;
48655         if(this.el.dom != document.body){
48656             size = this.el.getSize();
48657         }else{
48658             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
48659         }
48660         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
48661         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
48662         return size;
48663     },
48664     
48665     /**
48666      * Returns the Element this layout is bound to.
48667      * @return {Roo.Element}
48668      */
48669     getEl : function(){
48670         return this.el;
48671     },
48672     
48673     /**
48674      * Returns the specified region.
48675      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
48676      * @return {Roo.LayoutRegion}
48677      */
48678     getRegion : function(target){
48679         return this.regions[target.toLowerCase()];
48680     },
48681     
48682     onWindowResize : function(){
48683         if(this.monitorWindowResize){
48684             this.layout();
48685         }
48686     }
48687 });/*
48688  * Based on:
48689  * Ext JS Library 1.1.1
48690  * Copyright(c) 2006-2007, Ext JS, LLC.
48691  *
48692  * Originally Released Under LGPL - original licence link has changed is not relivant.
48693  *
48694  * Fork - LGPL
48695  * <script type="text/javascript">
48696  */
48697 /**
48698  * @class Roo.BorderLayout
48699  * @extends Roo.LayoutManager
48700  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
48701  * please see: <br><br>
48702  * <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>
48703  * <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>
48704  * Example:
48705  <pre><code>
48706  var layout = new Roo.BorderLayout(document.body, {
48707     north: {
48708         initialSize: 25,
48709         titlebar: false
48710     },
48711     west: {
48712         split:true,
48713         initialSize: 200,
48714         minSize: 175,
48715         maxSize: 400,
48716         titlebar: true,
48717         collapsible: true
48718     },
48719     east: {
48720         split:true,
48721         initialSize: 202,
48722         minSize: 175,
48723         maxSize: 400,
48724         titlebar: true,
48725         collapsible: true
48726     },
48727     south: {
48728         split:true,
48729         initialSize: 100,
48730         minSize: 100,
48731         maxSize: 200,
48732         titlebar: true,
48733         collapsible: true
48734     },
48735     center: {
48736         titlebar: true,
48737         autoScroll:true,
48738         resizeTabs: true,
48739         minTabWidth: 50,
48740         preferredTabWidth: 150
48741     }
48742 });
48743
48744 // shorthand
48745 var CP = Roo.ContentPanel;
48746
48747 layout.beginUpdate();
48748 layout.add("north", new CP("north", "North"));
48749 layout.add("south", new CP("south", {title: "South", closable: true}));
48750 layout.add("west", new CP("west", {title: "West"}));
48751 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48752 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48753 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48754 layout.getRegion("center").showPanel("center1");
48755 layout.endUpdate();
48756 </code></pre>
48757
48758 <b>The container the layout is rendered into can be either the body element or any other element.
48759 If it is not the body element, the container needs to either be an absolute positioned element,
48760 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48761 the container size if it is not the body element.</b>
48762
48763 * @constructor
48764 * Create a new BorderLayout
48765 * @param {String/HTMLElement/Element} container The container this layout is bound to
48766 * @param {Object} config Configuration options
48767  */
48768 Roo.BorderLayout = function(container, config){
48769     config = config || {};
48770     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48771     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48772     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48773         var target = this.factory.validRegions[i];
48774         if(config[target]){
48775             this.addRegion(target, config[target]);
48776         }
48777     }
48778 };
48779
48780 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48781     /**
48782      * Creates and adds a new region if it doesn't already exist.
48783      * @param {String} target The target region key (north, south, east, west or center).
48784      * @param {Object} config The regions config object
48785      * @return {BorderLayoutRegion} The new region
48786      */
48787     addRegion : function(target, config){
48788         if(!this.regions[target]){
48789             var r = this.factory.create(target, this, config);
48790             this.bindRegion(target, r);
48791         }
48792         return this.regions[target];
48793     },
48794
48795     // private (kinda)
48796     bindRegion : function(name, r){
48797         this.regions[name] = r;
48798         r.on("visibilitychange", this.layout, this);
48799         r.on("paneladded", this.layout, this);
48800         r.on("panelremoved", this.layout, this);
48801         r.on("invalidated", this.layout, this);
48802         r.on("resized", this.onRegionResized, this);
48803         r.on("collapsed", this.onRegionCollapsed, this);
48804         r.on("expanded", this.onRegionExpanded, this);
48805     },
48806
48807     /**
48808      * Performs a layout update.
48809      */
48810     layout : function(){
48811         if(this.updating) return;
48812         var size = this.getViewSize();
48813         var w = size.width;
48814         var h = size.height;
48815         var centerW = w;
48816         var centerH = h;
48817         var centerY = 0;
48818         var centerX = 0;
48819         //var x = 0, y = 0;
48820
48821         var rs = this.regions;
48822         var north = rs["north"];
48823         var south = rs["south"]; 
48824         var west = rs["west"];
48825         var east = rs["east"];
48826         var center = rs["center"];
48827         //if(this.hideOnLayout){ // not supported anymore
48828             //c.el.setStyle("display", "none");
48829         //}
48830         if(north && north.isVisible()){
48831             var b = north.getBox();
48832             var m = north.getMargins();
48833             b.width = w - (m.left+m.right);
48834             b.x = m.left;
48835             b.y = m.top;
48836             centerY = b.height + b.y + m.bottom;
48837             centerH -= centerY;
48838             north.updateBox(this.safeBox(b));
48839         }
48840         if(south && south.isVisible()){
48841             var b = south.getBox();
48842             var m = south.getMargins();
48843             b.width = w - (m.left+m.right);
48844             b.x = m.left;
48845             var totalHeight = (b.height + m.top + m.bottom);
48846             b.y = h - totalHeight + m.top;
48847             centerH -= totalHeight;
48848             south.updateBox(this.safeBox(b));
48849         }
48850         if(west && west.isVisible()){
48851             var b = west.getBox();
48852             var m = west.getMargins();
48853             b.height = centerH - (m.top+m.bottom);
48854             b.x = m.left;
48855             b.y = centerY + m.top;
48856             var totalWidth = (b.width + m.left + m.right);
48857             centerX += totalWidth;
48858             centerW -= totalWidth;
48859             west.updateBox(this.safeBox(b));
48860         }
48861         if(east && east.isVisible()){
48862             var b = east.getBox();
48863             var m = east.getMargins();
48864             b.height = centerH - (m.top+m.bottom);
48865             var totalWidth = (b.width + m.left + m.right);
48866             b.x = w - totalWidth + m.left;
48867             b.y = centerY + m.top;
48868             centerW -= totalWidth;
48869             east.updateBox(this.safeBox(b));
48870         }
48871         if(center){
48872             var m = center.getMargins();
48873             var centerBox = {
48874                 x: centerX + m.left,
48875                 y: centerY + m.top,
48876                 width: centerW - (m.left+m.right),
48877                 height: centerH - (m.top+m.bottom)
48878             };
48879             //if(this.hideOnLayout){
48880                 //center.el.setStyle("display", "block");
48881             //}
48882             center.updateBox(this.safeBox(centerBox));
48883         }
48884         this.el.repaint();
48885         this.fireEvent("layout", this);
48886     },
48887
48888     // private
48889     safeBox : function(box){
48890         box.width = Math.max(0, box.width);
48891         box.height = Math.max(0, box.height);
48892         return box;
48893     },
48894
48895     /**
48896      * Adds a ContentPanel (or subclass) to this layout.
48897      * @param {String} target The target region key (north, south, east, west or center).
48898      * @param {Roo.ContentPanel} panel The panel to add
48899      * @return {Roo.ContentPanel} The added panel
48900      */
48901     add : function(target, panel){
48902          
48903         target = target.toLowerCase();
48904         return this.regions[target].add(panel);
48905     },
48906
48907     /**
48908      * Remove a ContentPanel (or subclass) to this layout.
48909      * @param {String} target The target region key (north, south, east, west or center).
48910      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48911      * @return {Roo.ContentPanel} The removed panel
48912      */
48913     remove : function(target, panel){
48914         target = target.toLowerCase();
48915         return this.regions[target].remove(panel);
48916     },
48917
48918     /**
48919      * Searches all regions for a panel with the specified id
48920      * @param {String} panelId
48921      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48922      */
48923     findPanel : function(panelId){
48924         var rs = this.regions;
48925         for(var target in rs){
48926             if(typeof rs[target] != "function"){
48927                 var p = rs[target].getPanel(panelId);
48928                 if(p){
48929                     return p;
48930                 }
48931             }
48932         }
48933         return null;
48934     },
48935
48936     /**
48937      * Searches all regions for a panel with the specified id and activates (shows) it.
48938      * @param {String/ContentPanel} panelId The panels id or the panel itself
48939      * @return {Roo.ContentPanel} The shown panel or null
48940      */
48941     showPanel : function(panelId) {
48942       var rs = this.regions;
48943       for(var target in rs){
48944          var r = rs[target];
48945          if(typeof r != "function"){
48946             if(r.hasPanel(panelId)){
48947                return r.showPanel(panelId);
48948             }
48949          }
48950       }
48951       return null;
48952    },
48953
48954    /**
48955      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48956      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48957      */
48958     restoreState : function(provider){
48959         if(!provider){
48960             provider = Roo.state.Manager;
48961         }
48962         var sm = new Roo.LayoutStateManager();
48963         sm.init(this, provider);
48964     },
48965
48966     /**
48967      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48968      * object should contain properties for each region to add ContentPanels to, and each property's value should be
48969      * a valid ContentPanel config object.  Example:
48970      * <pre><code>
48971 // Create the main layout
48972 var layout = new Roo.BorderLayout('main-ct', {
48973     west: {
48974         split:true,
48975         minSize: 175,
48976         titlebar: true
48977     },
48978     center: {
48979         title:'Components'
48980     }
48981 }, 'main-ct');
48982
48983 // Create and add multiple ContentPanels at once via configs
48984 layout.batchAdd({
48985    west: {
48986        id: 'source-files',
48987        autoCreate:true,
48988        title:'Ext Source Files',
48989        autoScroll:true,
48990        fitToFrame:true
48991    },
48992    center : {
48993        el: cview,
48994        autoScroll:true,
48995        fitToFrame:true,
48996        toolbar: tb,
48997        resizeEl:'cbody'
48998    }
48999 });
49000 </code></pre>
49001      * @param {Object} regions An object containing ContentPanel configs by region name
49002      */
49003     batchAdd : function(regions){
49004         this.beginUpdate();
49005         for(var rname in regions){
49006             var lr = this.regions[rname];
49007             if(lr){
49008                 this.addTypedPanels(lr, regions[rname]);
49009             }
49010         }
49011         this.endUpdate();
49012     },
49013
49014     // private
49015     addTypedPanels : function(lr, ps){
49016         if(typeof ps == 'string'){
49017             lr.add(new Roo.ContentPanel(ps));
49018         }
49019         else if(ps instanceof Array){
49020             for(var i =0, len = ps.length; i < len; i++){
49021                 this.addTypedPanels(lr, ps[i]);
49022             }
49023         }
49024         else if(!ps.events){ // raw config?
49025             var el = ps.el;
49026             delete ps.el; // prevent conflict
49027             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49028         }
49029         else {  // panel object assumed!
49030             lr.add(ps);
49031         }
49032     },
49033     /**
49034      * Adds a xtype elements to the layout.
49035      * <pre><code>
49036
49037 layout.addxtype({
49038        xtype : 'ContentPanel',
49039        region: 'west',
49040        items: [ .... ]
49041    }
49042 );
49043
49044 layout.addxtype({
49045         xtype : 'NestedLayoutPanel',
49046         region: 'west',
49047         layout: {
49048            center: { },
49049            west: { }   
49050         },
49051         items : [ ... list of content panels or nested layout panels.. ]
49052    }
49053 );
49054 </code></pre>
49055      * @param {Object} cfg Xtype definition of item to add.
49056      */
49057     addxtype : function(cfg)
49058     {
49059         // basically accepts a pannel...
49060         // can accept a layout region..!?!?
49061         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49062         
49063         if (!cfg.xtype.match(/Panel$/)) {
49064             return false;
49065         }
49066         var ret = false;
49067         
49068         if (typeof(cfg.region) == 'undefined') {
49069             Roo.log("Failed to add Panel, region was not set");
49070             Roo.log(cfg);
49071             return false;
49072         }
49073         var region = cfg.region;
49074         delete cfg.region;
49075         
49076           
49077         var xitems = [];
49078         if (cfg.items) {
49079             xitems = cfg.items;
49080             delete cfg.items;
49081         }
49082         var nb = false;
49083         
49084         switch(cfg.xtype) 
49085         {
49086             case 'ContentPanel':  // ContentPanel (el, cfg)
49087             case 'ScrollPanel':  // ContentPanel (el, cfg)
49088             case 'ViewPanel': 
49089                 if(cfg.autoCreate) {
49090                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49091                 } else {
49092                     var el = this.el.createChild();
49093                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49094                 }
49095                 
49096                 this.add(region, ret);
49097                 break;
49098             
49099             
49100             case 'TreePanel': // our new panel!
49101                 cfg.el = this.el.createChild();
49102                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49103                 this.add(region, ret);
49104                 break;
49105             
49106             case 'NestedLayoutPanel': 
49107                 // create a new Layout (which is  a Border Layout...
49108                 var el = this.el.createChild();
49109                 var clayout = cfg.layout;
49110                 delete cfg.layout;
49111                 clayout.items   = clayout.items  || [];
49112                 // replace this exitems with the clayout ones..
49113                 xitems = clayout.items;
49114                  
49115                 
49116                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49117                     cfg.background = false;
49118                 }
49119                 var layout = new Roo.BorderLayout(el, clayout);
49120                 
49121                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49122                 //console.log('adding nested layout panel '  + cfg.toSource());
49123                 this.add(region, ret);
49124                 nb = {}; /// find first...
49125                 break;
49126                 
49127             case 'GridPanel': 
49128             
49129                 // needs grid and region
49130                 
49131                 //var el = this.getRegion(region).el.createChild();
49132                 var el = this.el.createChild();
49133                 // create the grid first...
49134                 
49135                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49136                 delete cfg.grid;
49137                 if (region == 'center' && this.active ) {
49138                     cfg.background = false;
49139                 }
49140                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49141                 
49142                 this.add(region, ret);
49143                 if (cfg.background) {
49144                     ret.on('activate', function(gp) {
49145                         if (!gp.grid.rendered) {
49146                             gp.grid.render();
49147                         }
49148                     });
49149                 } else {
49150                     grid.render();
49151                 }
49152                 break;
49153            
49154            
49155            
49156                 
49157                 
49158                 
49159             default:
49160                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49161                     
49162                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49163                     this.add(region, ret);
49164                 } else {
49165                 
49166                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49167                     return null;
49168                 }
49169                 
49170              // GridPanel (grid, cfg)
49171             
49172         }
49173         this.beginUpdate();
49174         // add children..
49175         var region = '';
49176         var abn = {};
49177         Roo.each(xitems, function(i)  {
49178             region = nb && i.region ? i.region : false;
49179             
49180             var add = ret.addxtype(i);
49181            
49182             if (region) {
49183                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49184                 if (!i.background) {
49185                     abn[region] = nb[region] ;
49186                 }
49187             }
49188             
49189         });
49190         this.endUpdate();
49191
49192         // make the last non-background panel active..
49193         //if (nb) { Roo.log(abn); }
49194         if (nb) {
49195             
49196             for(var r in abn) {
49197                 region = this.getRegion(r);
49198                 if (region) {
49199                     // tried using nb[r], but it does not work..
49200                      
49201                     region.showPanel(abn[r]);
49202                    
49203                 }
49204             }
49205         }
49206         return ret;
49207         
49208     }
49209 });
49210
49211 /**
49212  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49213  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49214  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49215  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49216  * <pre><code>
49217 // shorthand
49218 var CP = Roo.ContentPanel;
49219
49220 var layout = Roo.BorderLayout.create({
49221     north: {
49222         initialSize: 25,
49223         titlebar: false,
49224         panels: [new CP("north", "North")]
49225     },
49226     west: {
49227         split:true,
49228         initialSize: 200,
49229         minSize: 175,
49230         maxSize: 400,
49231         titlebar: true,
49232         collapsible: true,
49233         panels: [new CP("west", {title: "West"})]
49234     },
49235     east: {
49236         split:true,
49237         initialSize: 202,
49238         minSize: 175,
49239         maxSize: 400,
49240         titlebar: true,
49241         collapsible: true,
49242         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49243     },
49244     south: {
49245         split:true,
49246         initialSize: 100,
49247         minSize: 100,
49248         maxSize: 200,
49249         titlebar: true,
49250         collapsible: true,
49251         panels: [new CP("south", {title: "South", closable: true})]
49252     },
49253     center: {
49254         titlebar: true,
49255         autoScroll:true,
49256         resizeTabs: true,
49257         minTabWidth: 50,
49258         preferredTabWidth: 150,
49259         panels: [
49260             new CP("center1", {title: "Close Me", closable: true}),
49261             new CP("center2", {title: "Center Panel", closable: false})
49262         ]
49263     }
49264 }, document.body);
49265
49266 layout.getRegion("center").showPanel("center1");
49267 </code></pre>
49268  * @param config
49269  * @param targetEl
49270  */
49271 Roo.BorderLayout.create = function(config, targetEl){
49272     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49273     layout.beginUpdate();
49274     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49275     for(var j = 0, jlen = regions.length; j < jlen; j++){
49276         var lr = regions[j];
49277         if(layout.regions[lr] && config[lr].panels){
49278             var r = layout.regions[lr];
49279             var ps = config[lr].panels;
49280             layout.addTypedPanels(r, ps);
49281         }
49282     }
49283     layout.endUpdate();
49284     return layout;
49285 };
49286
49287 // private
49288 Roo.BorderLayout.RegionFactory = {
49289     // private
49290     validRegions : ["north","south","east","west","center"],
49291
49292     // private
49293     create : function(target, mgr, config){
49294         target = target.toLowerCase();
49295         if(config.lightweight || config.basic){
49296             return new Roo.BasicLayoutRegion(mgr, config, target);
49297         }
49298         switch(target){
49299             case "north":
49300                 return new Roo.NorthLayoutRegion(mgr, config);
49301             case "south":
49302                 return new Roo.SouthLayoutRegion(mgr, config);
49303             case "east":
49304                 return new Roo.EastLayoutRegion(mgr, config);
49305             case "west":
49306                 return new Roo.WestLayoutRegion(mgr, config);
49307             case "center":
49308                 return new Roo.CenterLayoutRegion(mgr, config);
49309         }
49310         throw 'Layout region "'+target+'" not supported.';
49311     }
49312 };/*
49313  * Based on:
49314  * Ext JS Library 1.1.1
49315  * Copyright(c) 2006-2007, Ext JS, LLC.
49316  *
49317  * Originally Released Under LGPL - original licence link has changed is not relivant.
49318  *
49319  * Fork - LGPL
49320  * <script type="text/javascript">
49321  */
49322  
49323 /**
49324  * @class Roo.BasicLayoutRegion
49325  * @extends Roo.util.Observable
49326  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49327  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49328  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49329  */
49330 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49331     this.mgr = mgr;
49332     this.position  = pos;
49333     this.events = {
49334         /**
49335          * @scope Roo.BasicLayoutRegion
49336          */
49337         
49338         /**
49339          * @event beforeremove
49340          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49341          * @param {Roo.LayoutRegion} this
49342          * @param {Roo.ContentPanel} panel The panel
49343          * @param {Object} e The cancel event object
49344          */
49345         "beforeremove" : true,
49346         /**
49347          * @event invalidated
49348          * Fires when the layout for this region is changed.
49349          * @param {Roo.LayoutRegion} this
49350          */
49351         "invalidated" : true,
49352         /**
49353          * @event visibilitychange
49354          * Fires when this region is shown or hidden 
49355          * @param {Roo.LayoutRegion} this
49356          * @param {Boolean} visibility true or false
49357          */
49358         "visibilitychange" : true,
49359         /**
49360          * @event paneladded
49361          * Fires when a panel is added. 
49362          * @param {Roo.LayoutRegion} this
49363          * @param {Roo.ContentPanel} panel The panel
49364          */
49365         "paneladded" : true,
49366         /**
49367          * @event panelremoved
49368          * Fires when a panel is removed. 
49369          * @param {Roo.LayoutRegion} this
49370          * @param {Roo.ContentPanel} panel The panel
49371          */
49372         "panelremoved" : true,
49373         /**
49374          * @event collapsed
49375          * Fires when this region is collapsed.
49376          * @param {Roo.LayoutRegion} this
49377          */
49378         "collapsed" : true,
49379         /**
49380          * @event expanded
49381          * Fires when this region is expanded.
49382          * @param {Roo.LayoutRegion} this
49383          */
49384         "expanded" : true,
49385         /**
49386          * @event slideshow
49387          * Fires when this region is slid into view.
49388          * @param {Roo.LayoutRegion} this
49389          */
49390         "slideshow" : true,
49391         /**
49392          * @event slidehide
49393          * Fires when this region slides out of view. 
49394          * @param {Roo.LayoutRegion} this
49395          */
49396         "slidehide" : true,
49397         /**
49398          * @event panelactivated
49399          * Fires when a panel is activated. 
49400          * @param {Roo.LayoutRegion} this
49401          * @param {Roo.ContentPanel} panel The activated panel
49402          */
49403         "panelactivated" : true,
49404         /**
49405          * @event resized
49406          * Fires when the user resizes this region. 
49407          * @param {Roo.LayoutRegion} this
49408          * @param {Number} newSize The new size (width for east/west, height for north/south)
49409          */
49410         "resized" : true
49411     };
49412     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49413     this.panels = new Roo.util.MixedCollection();
49414     this.panels.getKey = this.getPanelId.createDelegate(this);
49415     this.box = null;
49416     this.activePanel = null;
49417     // ensure listeners are added...
49418     
49419     if (config.listeners || config.events) {
49420         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49421             listeners : config.listeners || {},
49422             events : config.events || {}
49423         });
49424     }
49425     
49426     if(skipConfig !== true){
49427         this.applyConfig(config);
49428     }
49429 };
49430
49431 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49432     getPanelId : function(p){
49433         return p.getId();
49434     },
49435     
49436     applyConfig : function(config){
49437         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49438         this.config = config;
49439         
49440     },
49441     
49442     /**
49443      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49444      * the width, for horizontal (north, south) the height.
49445      * @param {Number} newSize The new width or height
49446      */
49447     resizeTo : function(newSize){
49448         var el = this.el ? this.el :
49449                  (this.activePanel ? this.activePanel.getEl() : null);
49450         if(el){
49451             switch(this.position){
49452                 case "east":
49453                 case "west":
49454                     el.setWidth(newSize);
49455                     this.fireEvent("resized", this, newSize);
49456                 break;
49457                 case "north":
49458                 case "south":
49459                     el.setHeight(newSize);
49460                     this.fireEvent("resized", this, newSize);
49461                 break;                
49462             }
49463         }
49464     },
49465     
49466     getBox : function(){
49467         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49468     },
49469     
49470     getMargins : function(){
49471         return this.margins;
49472     },
49473     
49474     updateBox : function(box){
49475         this.box = box;
49476         var el = this.activePanel.getEl();
49477         el.dom.style.left = box.x + "px";
49478         el.dom.style.top = box.y + "px";
49479         this.activePanel.setSize(box.width, box.height);
49480     },
49481     
49482     /**
49483      * Returns the container element for this region.
49484      * @return {Roo.Element}
49485      */
49486     getEl : function(){
49487         return this.activePanel;
49488     },
49489     
49490     /**
49491      * Returns true if this region is currently visible.
49492      * @return {Boolean}
49493      */
49494     isVisible : function(){
49495         return this.activePanel ? true : false;
49496     },
49497     
49498     setActivePanel : function(panel){
49499         panel = this.getPanel(panel);
49500         if(this.activePanel && this.activePanel != panel){
49501             this.activePanel.setActiveState(false);
49502             this.activePanel.getEl().setLeftTop(-10000,-10000);
49503         }
49504         this.activePanel = panel;
49505         panel.setActiveState(true);
49506         if(this.box){
49507             panel.setSize(this.box.width, this.box.height);
49508         }
49509         this.fireEvent("panelactivated", this, panel);
49510         this.fireEvent("invalidated");
49511     },
49512     
49513     /**
49514      * Show the specified panel.
49515      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49516      * @return {Roo.ContentPanel} The shown panel or null
49517      */
49518     showPanel : function(panel){
49519         if(panel = this.getPanel(panel)){
49520             this.setActivePanel(panel);
49521         }
49522         return panel;
49523     },
49524     
49525     /**
49526      * Get the active panel for this region.
49527      * @return {Roo.ContentPanel} The active panel or null
49528      */
49529     getActivePanel : function(){
49530         return this.activePanel;
49531     },
49532     
49533     /**
49534      * Add the passed ContentPanel(s)
49535      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49536      * @return {Roo.ContentPanel} The panel added (if only one was added)
49537      */
49538     add : function(panel){
49539         if(arguments.length > 1){
49540             for(var i = 0, len = arguments.length; i < len; i++) {
49541                 this.add(arguments[i]);
49542             }
49543             return null;
49544         }
49545         if(this.hasPanel(panel)){
49546             this.showPanel(panel);
49547             return panel;
49548         }
49549         var el = panel.getEl();
49550         if(el.dom.parentNode != this.mgr.el.dom){
49551             this.mgr.el.dom.appendChild(el.dom);
49552         }
49553         if(panel.setRegion){
49554             panel.setRegion(this);
49555         }
49556         this.panels.add(panel);
49557         el.setStyle("position", "absolute");
49558         if(!panel.background){
49559             this.setActivePanel(panel);
49560             if(this.config.initialSize && this.panels.getCount()==1){
49561                 this.resizeTo(this.config.initialSize);
49562             }
49563         }
49564         this.fireEvent("paneladded", this, panel);
49565         return panel;
49566     },
49567     
49568     /**
49569      * Returns true if the panel is in this region.
49570      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49571      * @return {Boolean}
49572      */
49573     hasPanel : function(panel){
49574         if(typeof panel == "object"){ // must be panel obj
49575             panel = panel.getId();
49576         }
49577         return this.getPanel(panel) ? true : false;
49578     },
49579     
49580     /**
49581      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49582      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49583      * @param {Boolean} preservePanel Overrides the config preservePanel option
49584      * @return {Roo.ContentPanel} The panel that was removed
49585      */
49586     remove : function(panel, preservePanel){
49587         panel = this.getPanel(panel);
49588         if(!panel){
49589             return null;
49590         }
49591         var e = {};
49592         this.fireEvent("beforeremove", this, panel, e);
49593         if(e.cancel === true){
49594             return null;
49595         }
49596         var panelId = panel.getId();
49597         this.panels.removeKey(panelId);
49598         return panel;
49599     },
49600     
49601     /**
49602      * Returns the panel specified or null if it's not in this region.
49603      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49604      * @return {Roo.ContentPanel}
49605      */
49606     getPanel : function(id){
49607         if(typeof id == "object"){ // must be panel obj
49608             return id;
49609         }
49610         return this.panels.get(id);
49611     },
49612     
49613     /**
49614      * Returns this regions position (north/south/east/west/center).
49615      * @return {String} 
49616      */
49617     getPosition: function(){
49618         return this.position;    
49619     }
49620 });/*
49621  * Based on:
49622  * Ext JS Library 1.1.1
49623  * Copyright(c) 2006-2007, Ext JS, LLC.
49624  *
49625  * Originally Released Under LGPL - original licence link has changed is not relivant.
49626  *
49627  * Fork - LGPL
49628  * <script type="text/javascript">
49629  */
49630  
49631 /**
49632  * @class Roo.LayoutRegion
49633  * @extends Roo.BasicLayoutRegion
49634  * This class represents a region in a layout manager.
49635  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
49636  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
49637  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
49638  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
49639  * @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})
49640  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
49641  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
49642  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
49643  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
49644  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
49645  * @cfg {String}    title           The title for the region (overrides panel titles)
49646  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
49647  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
49648  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
49649  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
49650  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
49651  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
49652  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
49653  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
49654  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
49655  * @cfg {Boolean}   showPin         True to show a pin button
49656  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
49657  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
49658  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
49659  * @cfg {Number}    width           For East/West panels
49660  * @cfg {Number}    height          For North/South panels
49661  * @cfg {Boolean}   split           To show the splitter
49662  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
49663  */
49664 Roo.LayoutRegion = function(mgr, config, pos){
49665     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
49666     var dh = Roo.DomHelper;
49667     /** This region's container element 
49668     * @type Roo.Element */
49669     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
49670     /** This region's title element 
49671     * @type Roo.Element */
49672
49673     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
49674         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
49675         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
49676     ]}, true);
49677     this.titleEl.enableDisplayMode();
49678     /** This region's title text element 
49679     * @type HTMLElement */
49680     this.titleTextEl = this.titleEl.dom.firstChild;
49681     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
49682     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
49683     this.closeBtn.enableDisplayMode();
49684     this.closeBtn.on("click", this.closeClicked, this);
49685     this.closeBtn.hide();
49686
49687     this.createBody(config);
49688     this.visible = true;
49689     this.collapsed = false;
49690
49691     if(config.hideWhenEmpty){
49692         this.hide();
49693         this.on("paneladded", this.validateVisibility, this);
49694         this.on("panelremoved", this.validateVisibility, this);
49695     }
49696     this.applyConfig(config);
49697 };
49698
49699 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
49700
49701     createBody : function(){
49702         /** This region's body element 
49703         * @type Roo.Element */
49704         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
49705     },
49706
49707     applyConfig : function(c){
49708         if(c.collapsible && this.position != "center" && !this.collapsedEl){
49709             var dh = Roo.DomHelper;
49710             if(c.titlebar !== false){
49711                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
49712                 this.collapseBtn.on("click", this.collapse, this);
49713                 this.collapseBtn.enableDisplayMode();
49714
49715                 if(c.showPin === true || this.showPin){
49716                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
49717                     this.stickBtn.enableDisplayMode();
49718                     this.stickBtn.on("click", this.expand, this);
49719                     this.stickBtn.hide();
49720                 }
49721             }
49722             /** This region's collapsed element
49723             * @type Roo.Element */
49724             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
49725                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
49726             ]}, true);
49727             if(c.floatable !== false){
49728                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
49729                this.collapsedEl.on("click", this.collapseClick, this);
49730             }
49731
49732             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
49733                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
49734                    id: "message", unselectable: "on", style:{"float":"left"}});
49735                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
49736              }
49737             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
49738             this.expandBtn.on("click", this.expand, this);
49739         }
49740         if(this.collapseBtn){
49741             this.collapseBtn.setVisible(c.collapsible == true);
49742         }
49743         this.cmargins = c.cmargins || this.cmargins ||
49744                          (this.position == "west" || this.position == "east" ?
49745                              {top: 0, left: 2, right:2, bottom: 0} :
49746                              {top: 2, left: 0, right:0, bottom: 2});
49747         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49748         this.bottomTabs = c.tabPosition != "top";
49749         this.autoScroll = c.autoScroll || false;
49750         if(this.autoScroll){
49751             this.bodyEl.setStyle("overflow", "auto");
49752         }else{
49753             this.bodyEl.setStyle("overflow", "hidden");
49754         }
49755         //if(c.titlebar !== false){
49756             if((!c.titlebar && !c.title) || c.titlebar === false){
49757                 this.titleEl.hide();
49758             }else{
49759                 this.titleEl.show();
49760                 if(c.title){
49761                     this.titleTextEl.innerHTML = c.title;
49762                 }
49763             }
49764         //}
49765         this.duration = c.duration || .30;
49766         this.slideDuration = c.slideDuration || .45;
49767         this.config = c;
49768         if(c.collapsed){
49769             this.collapse(true);
49770         }
49771         if(c.hidden){
49772             this.hide();
49773         }
49774     },
49775     /**
49776      * Returns true if this region is currently visible.
49777      * @return {Boolean}
49778      */
49779     isVisible : function(){
49780         return this.visible;
49781     },
49782
49783     /**
49784      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49785      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49786      */
49787     setCollapsedTitle : function(title){
49788         title = title || "&#160;";
49789         if(this.collapsedTitleTextEl){
49790             this.collapsedTitleTextEl.innerHTML = title;
49791         }
49792     },
49793
49794     getBox : function(){
49795         var b;
49796         if(!this.collapsed){
49797             b = this.el.getBox(false, true);
49798         }else{
49799             b = this.collapsedEl.getBox(false, true);
49800         }
49801         return b;
49802     },
49803
49804     getMargins : function(){
49805         return this.collapsed ? this.cmargins : this.margins;
49806     },
49807
49808     highlight : function(){
49809         this.el.addClass("x-layout-panel-dragover");
49810     },
49811
49812     unhighlight : function(){
49813         this.el.removeClass("x-layout-panel-dragover");
49814     },
49815
49816     updateBox : function(box){
49817         this.box = box;
49818         if(!this.collapsed){
49819             this.el.dom.style.left = box.x + "px";
49820             this.el.dom.style.top = box.y + "px";
49821             this.updateBody(box.width, box.height);
49822         }else{
49823             this.collapsedEl.dom.style.left = box.x + "px";
49824             this.collapsedEl.dom.style.top = box.y + "px";
49825             this.collapsedEl.setSize(box.width, box.height);
49826         }
49827         if(this.tabs){
49828             this.tabs.autoSizeTabs();
49829         }
49830     },
49831
49832     updateBody : function(w, h){
49833         if(w !== null){
49834             this.el.setWidth(w);
49835             w -= this.el.getBorderWidth("rl");
49836             if(this.config.adjustments){
49837                 w += this.config.adjustments[0];
49838             }
49839         }
49840         if(h !== null){
49841             this.el.setHeight(h);
49842             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49843             h -= this.el.getBorderWidth("tb");
49844             if(this.config.adjustments){
49845                 h += this.config.adjustments[1];
49846             }
49847             this.bodyEl.setHeight(h);
49848             if(this.tabs){
49849                 h = this.tabs.syncHeight(h);
49850             }
49851         }
49852         if(this.panelSize){
49853             w = w !== null ? w : this.panelSize.width;
49854             h = h !== null ? h : this.panelSize.height;
49855         }
49856         if(this.activePanel){
49857             var el = this.activePanel.getEl();
49858             w = w !== null ? w : el.getWidth();
49859             h = h !== null ? h : el.getHeight();
49860             this.panelSize = {width: w, height: h};
49861             this.activePanel.setSize(w, h);
49862         }
49863         if(Roo.isIE && this.tabs){
49864             this.tabs.el.repaint();
49865         }
49866     },
49867
49868     /**
49869      * Returns the container element for this region.
49870      * @return {Roo.Element}
49871      */
49872     getEl : function(){
49873         return this.el;
49874     },
49875
49876     /**
49877      * Hides this region.
49878      */
49879     hide : function(){
49880         if(!this.collapsed){
49881             this.el.dom.style.left = "-2000px";
49882             this.el.hide();
49883         }else{
49884             this.collapsedEl.dom.style.left = "-2000px";
49885             this.collapsedEl.hide();
49886         }
49887         this.visible = false;
49888         this.fireEvent("visibilitychange", this, false);
49889     },
49890
49891     /**
49892      * Shows this region if it was previously hidden.
49893      */
49894     show : function(){
49895         if(!this.collapsed){
49896             this.el.show();
49897         }else{
49898             this.collapsedEl.show();
49899         }
49900         this.visible = true;
49901         this.fireEvent("visibilitychange", this, true);
49902     },
49903
49904     closeClicked : function(){
49905         if(this.activePanel){
49906             this.remove(this.activePanel);
49907         }
49908     },
49909
49910     collapseClick : function(e){
49911         if(this.isSlid){
49912            e.stopPropagation();
49913            this.slideIn();
49914         }else{
49915            e.stopPropagation();
49916            this.slideOut();
49917         }
49918     },
49919
49920     /**
49921      * Collapses this region.
49922      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49923      */
49924     collapse : function(skipAnim){
49925         if(this.collapsed) return;
49926         this.collapsed = true;
49927         if(this.split){
49928             this.split.el.hide();
49929         }
49930         if(this.config.animate && skipAnim !== true){
49931             this.fireEvent("invalidated", this);
49932             this.animateCollapse();
49933         }else{
49934             this.el.setLocation(-20000,-20000);
49935             this.el.hide();
49936             this.collapsedEl.show();
49937             this.fireEvent("collapsed", this);
49938             this.fireEvent("invalidated", this);
49939         }
49940     },
49941
49942     animateCollapse : function(){
49943         // overridden
49944     },
49945
49946     /**
49947      * Expands this region if it was previously collapsed.
49948      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49949      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49950      */
49951     expand : function(e, skipAnim){
49952         if(e) e.stopPropagation();
49953         if(!this.collapsed || this.el.hasActiveFx()) return;
49954         if(this.isSlid){
49955             this.afterSlideIn();
49956             skipAnim = true;
49957         }
49958         this.collapsed = false;
49959         if(this.config.animate && skipAnim !== true){
49960             this.animateExpand();
49961         }else{
49962             this.el.show();
49963             if(this.split){
49964                 this.split.el.show();
49965             }
49966             this.collapsedEl.setLocation(-2000,-2000);
49967             this.collapsedEl.hide();
49968             this.fireEvent("invalidated", this);
49969             this.fireEvent("expanded", this);
49970         }
49971     },
49972
49973     animateExpand : function(){
49974         // overridden
49975     },
49976
49977     initTabs : function()
49978     {
49979         this.bodyEl.setStyle("overflow", "hidden");
49980         var ts = new Roo.TabPanel(
49981                 this.bodyEl.dom,
49982                 {
49983                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
49984                     disableTooltips: this.config.disableTabTips,
49985                     toolbar : this.config.toolbar
49986                 }
49987         );
49988         if(this.config.hideTabs){
49989             ts.stripWrap.setDisplayed(false);
49990         }
49991         this.tabs = ts;
49992         ts.resizeTabs = this.config.resizeTabs === true;
49993         ts.minTabWidth = this.config.minTabWidth || 40;
49994         ts.maxTabWidth = this.config.maxTabWidth || 250;
49995         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
49996         ts.monitorResize = false;
49997         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49998         ts.bodyEl.addClass('x-layout-tabs-body');
49999         this.panels.each(this.initPanelAsTab, this);
50000     },
50001
50002     initPanelAsTab : function(panel){
50003         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50004                     this.config.closeOnTab && panel.isClosable());
50005         if(panel.tabTip !== undefined){
50006             ti.setTooltip(panel.tabTip);
50007         }
50008         ti.on("activate", function(){
50009               this.setActivePanel(panel);
50010         }, this);
50011         if(this.config.closeOnTab){
50012             ti.on("beforeclose", function(t, e){
50013                 e.cancel = true;
50014                 this.remove(panel);
50015             }, this);
50016         }
50017         return ti;
50018     },
50019
50020     updatePanelTitle : function(panel, title){
50021         if(this.activePanel == panel){
50022             this.updateTitle(title);
50023         }
50024         if(this.tabs){
50025             var ti = this.tabs.getTab(panel.getEl().id);
50026             ti.setText(title);
50027             if(panel.tabTip !== undefined){
50028                 ti.setTooltip(panel.tabTip);
50029             }
50030         }
50031     },
50032
50033     updateTitle : function(title){
50034         if(this.titleTextEl && !this.config.title){
50035             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50036         }
50037     },
50038
50039     setActivePanel : function(panel){
50040         panel = this.getPanel(panel);
50041         if(this.activePanel && this.activePanel != panel){
50042             this.activePanel.setActiveState(false);
50043         }
50044         this.activePanel = panel;
50045         panel.setActiveState(true);
50046         if(this.panelSize){
50047             panel.setSize(this.panelSize.width, this.panelSize.height);
50048         }
50049         if(this.closeBtn){
50050             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50051         }
50052         this.updateTitle(panel.getTitle());
50053         if(this.tabs){
50054             this.fireEvent("invalidated", this);
50055         }
50056         this.fireEvent("panelactivated", this, panel);
50057     },
50058
50059     /**
50060      * Shows the specified panel.
50061      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50062      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50063      */
50064     showPanel : function(panel){
50065         if(panel = this.getPanel(panel)){
50066             if(this.tabs){
50067                 var tab = this.tabs.getTab(panel.getEl().id);
50068                 if(tab.isHidden()){
50069                     this.tabs.unhideTab(tab.id);
50070                 }
50071                 tab.activate();
50072             }else{
50073                 this.setActivePanel(panel);
50074             }
50075         }
50076         return panel;
50077     },
50078
50079     /**
50080      * Get the active panel for this region.
50081      * @return {Roo.ContentPanel} The active panel or null
50082      */
50083     getActivePanel : function(){
50084         return this.activePanel;
50085     },
50086
50087     validateVisibility : function(){
50088         if(this.panels.getCount() < 1){
50089             this.updateTitle("&#160;");
50090             this.closeBtn.hide();
50091             this.hide();
50092         }else{
50093             if(!this.isVisible()){
50094                 this.show();
50095             }
50096         }
50097     },
50098
50099     /**
50100      * Adds the passed ContentPanel(s) to this region.
50101      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50102      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50103      */
50104     add : function(panel){
50105         if(arguments.length > 1){
50106             for(var i = 0, len = arguments.length; i < len; i++) {
50107                 this.add(arguments[i]);
50108             }
50109             return null;
50110         }
50111         if(this.hasPanel(panel)){
50112             this.showPanel(panel);
50113             return panel;
50114         }
50115         panel.setRegion(this);
50116         this.panels.add(panel);
50117         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50118             this.bodyEl.dom.appendChild(panel.getEl().dom);
50119             if(panel.background !== true){
50120                 this.setActivePanel(panel);
50121             }
50122             this.fireEvent("paneladded", this, panel);
50123             return panel;
50124         }
50125         if(!this.tabs){
50126             this.initTabs();
50127         }else{
50128             this.initPanelAsTab(panel);
50129         }
50130         if(panel.background !== true){
50131             this.tabs.activate(panel.getEl().id);
50132         }
50133         this.fireEvent("paneladded", this, panel);
50134         return panel;
50135     },
50136
50137     /**
50138      * Hides the tab for the specified panel.
50139      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50140      */
50141     hidePanel : function(panel){
50142         if(this.tabs && (panel = this.getPanel(panel))){
50143             this.tabs.hideTab(panel.getEl().id);
50144         }
50145     },
50146
50147     /**
50148      * Unhides the tab for a previously hidden panel.
50149      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50150      */
50151     unhidePanel : function(panel){
50152         if(this.tabs && (panel = this.getPanel(panel))){
50153             this.tabs.unhideTab(panel.getEl().id);
50154         }
50155     },
50156
50157     clearPanels : function(){
50158         while(this.panels.getCount() > 0){
50159              this.remove(this.panels.first());
50160         }
50161     },
50162
50163     /**
50164      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50165      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50166      * @param {Boolean} preservePanel Overrides the config preservePanel option
50167      * @return {Roo.ContentPanel} The panel that was removed
50168      */
50169     remove : function(panel, preservePanel){
50170         panel = this.getPanel(panel);
50171         if(!panel){
50172             return null;
50173         }
50174         var e = {};
50175         this.fireEvent("beforeremove", this, panel, e);
50176         if(e.cancel === true){
50177             return null;
50178         }
50179         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50180         var panelId = panel.getId();
50181         this.panels.removeKey(panelId);
50182         if(preservePanel){
50183             document.body.appendChild(panel.getEl().dom);
50184         }
50185         if(this.tabs){
50186             this.tabs.removeTab(panel.getEl().id);
50187         }else if (!preservePanel){
50188             this.bodyEl.dom.removeChild(panel.getEl().dom);
50189         }
50190         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50191             var p = this.panels.first();
50192             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50193             tempEl.appendChild(p.getEl().dom);
50194             this.bodyEl.update("");
50195             this.bodyEl.dom.appendChild(p.getEl().dom);
50196             tempEl = null;
50197             this.updateTitle(p.getTitle());
50198             this.tabs = null;
50199             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50200             this.setActivePanel(p);
50201         }
50202         panel.setRegion(null);
50203         if(this.activePanel == panel){
50204             this.activePanel = null;
50205         }
50206         if(this.config.autoDestroy !== false && preservePanel !== true){
50207             try{panel.destroy();}catch(e){}
50208         }
50209         this.fireEvent("panelremoved", this, panel);
50210         return panel;
50211     },
50212
50213     /**
50214      * Returns the TabPanel component used by this region
50215      * @return {Roo.TabPanel}
50216      */
50217     getTabs : function(){
50218         return this.tabs;
50219     },
50220
50221     createTool : function(parentEl, className){
50222         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50223             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50224         btn.addClassOnOver("x-layout-tools-button-over");
50225         return btn;
50226     }
50227 });/*
50228  * Based on:
50229  * Ext JS Library 1.1.1
50230  * Copyright(c) 2006-2007, Ext JS, LLC.
50231  *
50232  * Originally Released Under LGPL - original licence link has changed is not relivant.
50233  *
50234  * Fork - LGPL
50235  * <script type="text/javascript">
50236  */
50237  
50238
50239
50240 /**
50241  * @class Roo.SplitLayoutRegion
50242  * @extends Roo.LayoutRegion
50243  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50244  */
50245 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50246     this.cursor = cursor;
50247     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50248 };
50249
50250 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50251     splitTip : "Drag to resize.",
50252     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50253     useSplitTips : false,
50254
50255     applyConfig : function(config){
50256         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50257         if(config.split){
50258             if(!this.split){
50259                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50260                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50261                 /** The SplitBar for this region 
50262                 * @type Roo.SplitBar */
50263                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50264                 this.split.on("moved", this.onSplitMove, this);
50265                 this.split.useShim = config.useShim === true;
50266                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50267                 if(this.useSplitTips){
50268                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50269                 }
50270                 if(config.collapsible){
50271                     this.split.el.on("dblclick", this.collapse,  this);
50272                 }
50273             }
50274             if(typeof config.minSize != "undefined"){
50275                 this.split.minSize = config.minSize;
50276             }
50277             if(typeof config.maxSize != "undefined"){
50278                 this.split.maxSize = config.maxSize;
50279             }
50280             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50281                 this.hideSplitter();
50282             }
50283         }
50284     },
50285
50286     getHMaxSize : function(){
50287          var cmax = this.config.maxSize || 10000;
50288          var center = this.mgr.getRegion("center");
50289          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50290     },
50291
50292     getVMaxSize : function(){
50293          var cmax = this.config.maxSize || 10000;
50294          var center = this.mgr.getRegion("center");
50295          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50296     },
50297
50298     onSplitMove : function(split, newSize){
50299         this.fireEvent("resized", this, newSize);
50300     },
50301     
50302     /** 
50303      * Returns the {@link Roo.SplitBar} for this region.
50304      * @return {Roo.SplitBar}
50305      */
50306     getSplitBar : function(){
50307         return this.split;
50308     },
50309     
50310     hide : function(){
50311         this.hideSplitter();
50312         Roo.SplitLayoutRegion.superclass.hide.call(this);
50313     },
50314
50315     hideSplitter : function(){
50316         if(this.split){
50317             this.split.el.setLocation(-2000,-2000);
50318             this.split.el.hide();
50319         }
50320     },
50321
50322     show : function(){
50323         if(this.split){
50324             this.split.el.show();
50325         }
50326         Roo.SplitLayoutRegion.superclass.show.call(this);
50327     },
50328     
50329     beforeSlide: function(){
50330         if(Roo.isGecko){// firefox overflow auto bug workaround
50331             this.bodyEl.clip();
50332             if(this.tabs) this.tabs.bodyEl.clip();
50333             if(this.activePanel){
50334                 this.activePanel.getEl().clip();
50335                 
50336                 if(this.activePanel.beforeSlide){
50337                     this.activePanel.beforeSlide();
50338                 }
50339             }
50340         }
50341     },
50342     
50343     afterSlide : function(){
50344         if(Roo.isGecko){// firefox overflow auto bug workaround
50345             this.bodyEl.unclip();
50346             if(this.tabs) this.tabs.bodyEl.unclip();
50347             if(this.activePanel){
50348                 this.activePanel.getEl().unclip();
50349                 if(this.activePanel.afterSlide){
50350                     this.activePanel.afterSlide();
50351                 }
50352             }
50353         }
50354     },
50355
50356     initAutoHide : function(){
50357         if(this.autoHide !== false){
50358             if(!this.autoHideHd){
50359                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50360                 this.autoHideHd = {
50361                     "mouseout": function(e){
50362                         if(!e.within(this.el, true)){
50363                             st.delay(500);
50364                         }
50365                     },
50366                     "mouseover" : function(e){
50367                         st.cancel();
50368                     },
50369                     scope : this
50370                 };
50371             }
50372             this.el.on(this.autoHideHd);
50373         }
50374     },
50375
50376     clearAutoHide : function(){
50377         if(this.autoHide !== false){
50378             this.el.un("mouseout", this.autoHideHd.mouseout);
50379             this.el.un("mouseover", this.autoHideHd.mouseover);
50380         }
50381     },
50382
50383     clearMonitor : function(){
50384         Roo.get(document).un("click", this.slideInIf, this);
50385     },
50386
50387     // these names are backwards but not changed for compat
50388     slideOut : function(){
50389         if(this.isSlid || this.el.hasActiveFx()){
50390             return;
50391         }
50392         this.isSlid = true;
50393         if(this.collapseBtn){
50394             this.collapseBtn.hide();
50395         }
50396         this.closeBtnState = this.closeBtn.getStyle('display');
50397         this.closeBtn.hide();
50398         if(this.stickBtn){
50399             this.stickBtn.show();
50400         }
50401         this.el.show();
50402         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50403         this.beforeSlide();
50404         this.el.setStyle("z-index", 10001);
50405         this.el.slideIn(this.getSlideAnchor(), {
50406             callback: function(){
50407                 this.afterSlide();
50408                 this.initAutoHide();
50409                 Roo.get(document).on("click", this.slideInIf, this);
50410                 this.fireEvent("slideshow", this);
50411             },
50412             scope: this,
50413             block: true
50414         });
50415     },
50416
50417     afterSlideIn : function(){
50418         this.clearAutoHide();
50419         this.isSlid = false;
50420         this.clearMonitor();
50421         this.el.setStyle("z-index", "");
50422         if(this.collapseBtn){
50423             this.collapseBtn.show();
50424         }
50425         this.closeBtn.setStyle('display', this.closeBtnState);
50426         if(this.stickBtn){
50427             this.stickBtn.hide();
50428         }
50429         this.fireEvent("slidehide", this);
50430     },
50431
50432     slideIn : function(cb){
50433         if(!this.isSlid || this.el.hasActiveFx()){
50434             Roo.callback(cb);
50435             return;
50436         }
50437         this.isSlid = false;
50438         this.beforeSlide();
50439         this.el.slideOut(this.getSlideAnchor(), {
50440             callback: function(){
50441                 this.el.setLeftTop(-10000, -10000);
50442                 this.afterSlide();
50443                 this.afterSlideIn();
50444                 Roo.callback(cb);
50445             },
50446             scope: this,
50447             block: true
50448         });
50449     },
50450     
50451     slideInIf : function(e){
50452         if(!e.within(this.el)){
50453             this.slideIn();
50454         }
50455     },
50456
50457     animateCollapse : function(){
50458         this.beforeSlide();
50459         this.el.setStyle("z-index", 20000);
50460         var anchor = this.getSlideAnchor();
50461         this.el.slideOut(anchor, {
50462             callback : function(){
50463                 this.el.setStyle("z-index", "");
50464                 this.collapsedEl.slideIn(anchor, {duration:.3});
50465                 this.afterSlide();
50466                 this.el.setLocation(-10000,-10000);
50467                 this.el.hide();
50468                 this.fireEvent("collapsed", this);
50469             },
50470             scope: this,
50471             block: true
50472         });
50473     },
50474
50475     animateExpand : function(){
50476         this.beforeSlide();
50477         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50478         this.el.setStyle("z-index", 20000);
50479         this.collapsedEl.hide({
50480             duration:.1
50481         });
50482         this.el.slideIn(this.getSlideAnchor(), {
50483             callback : function(){
50484                 this.el.setStyle("z-index", "");
50485                 this.afterSlide();
50486                 if(this.split){
50487                     this.split.el.show();
50488                 }
50489                 this.fireEvent("invalidated", this);
50490                 this.fireEvent("expanded", this);
50491             },
50492             scope: this,
50493             block: true
50494         });
50495     },
50496
50497     anchors : {
50498         "west" : "left",
50499         "east" : "right",
50500         "north" : "top",
50501         "south" : "bottom"
50502     },
50503
50504     sanchors : {
50505         "west" : "l",
50506         "east" : "r",
50507         "north" : "t",
50508         "south" : "b"
50509     },
50510
50511     canchors : {
50512         "west" : "tl-tr",
50513         "east" : "tr-tl",
50514         "north" : "tl-bl",
50515         "south" : "bl-tl"
50516     },
50517
50518     getAnchor : function(){
50519         return this.anchors[this.position];
50520     },
50521
50522     getCollapseAnchor : function(){
50523         return this.canchors[this.position];
50524     },
50525
50526     getSlideAnchor : function(){
50527         return this.sanchors[this.position];
50528     },
50529
50530     getAlignAdj : function(){
50531         var cm = this.cmargins;
50532         switch(this.position){
50533             case "west":
50534                 return [0, 0];
50535             break;
50536             case "east":
50537                 return [0, 0];
50538             break;
50539             case "north":
50540                 return [0, 0];
50541             break;
50542             case "south":
50543                 return [0, 0];
50544             break;
50545         }
50546     },
50547
50548     getExpandAdj : function(){
50549         var c = this.collapsedEl, cm = this.cmargins;
50550         switch(this.position){
50551             case "west":
50552                 return [-(cm.right+c.getWidth()+cm.left), 0];
50553             break;
50554             case "east":
50555                 return [cm.right+c.getWidth()+cm.left, 0];
50556             break;
50557             case "north":
50558                 return [0, -(cm.top+cm.bottom+c.getHeight())];
50559             break;
50560             case "south":
50561                 return [0, cm.top+cm.bottom+c.getHeight()];
50562             break;
50563         }
50564     }
50565 });/*
50566  * Based on:
50567  * Ext JS Library 1.1.1
50568  * Copyright(c) 2006-2007, Ext JS, LLC.
50569  *
50570  * Originally Released Under LGPL - original licence link has changed is not relivant.
50571  *
50572  * Fork - LGPL
50573  * <script type="text/javascript">
50574  */
50575 /*
50576  * These classes are private internal classes
50577  */
50578 Roo.CenterLayoutRegion = function(mgr, config){
50579     Roo.LayoutRegion.call(this, mgr, config, "center");
50580     this.visible = true;
50581     this.minWidth = config.minWidth || 20;
50582     this.minHeight = config.minHeight || 20;
50583 };
50584
50585 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
50586     hide : function(){
50587         // center panel can't be hidden
50588     },
50589     
50590     show : function(){
50591         // center panel can't be hidden
50592     },
50593     
50594     getMinWidth: function(){
50595         return this.minWidth;
50596     },
50597     
50598     getMinHeight: function(){
50599         return this.minHeight;
50600     }
50601 });
50602
50603
50604 Roo.NorthLayoutRegion = function(mgr, config){
50605     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
50606     if(this.split){
50607         this.split.placement = Roo.SplitBar.TOP;
50608         this.split.orientation = Roo.SplitBar.VERTICAL;
50609         this.split.el.addClass("x-layout-split-v");
50610     }
50611     var size = config.initialSize || config.height;
50612     if(typeof size != "undefined"){
50613         this.el.setHeight(size);
50614     }
50615 };
50616 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
50617     orientation: Roo.SplitBar.VERTICAL,
50618     getBox : function(){
50619         if(this.collapsed){
50620             return this.collapsedEl.getBox();
50621         }
50622         var box = this.el.getBox();
50623         if(this.split){
50624             box.height += this.split.el.getHeight();
50625         }
50626         return box;
50627     },
50628     
50629     updateBox : function(box){
50630         if(this.split && !this.collapsed){
50631             box.height -= this.split.el.getHeight();
50632             this.split.el.setLeft(box.x);
50633             this.split.el.setTop(box.y+box.height);
50634             this.split.el.setWidth(box.width);
50635         }
50636         if(this.collapsed){
50637             this.updateBody(box.width, null);
50638         }
50639         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50640     }
50641 });
50642
50643 Roo.SouthLayoutRegion = function(mgr, config){
50644     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
50645     if(this.split){
50646         this.split.placement = Roo.SplitBar.BOTTOM;
50647         this.split.orientation = Roo.SplitBar.VERTICAL;
50648         this.split.el.addClass("x-layout-split-v");
50649     }
50650     var size = config.initialSize || config.height;
50651     if(typeof size != "undefined"){
50652         this.el.setHeight(size);
50653     }
50654 };
50655 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
50656     orientation: Roo.SplitBar.VERTICAL,
50657     getBox : function(){
50658         if(this.collapsed){
50659             return this.collapsedEl.getBox();
50660         }
50661         var box = this.el.getBox();
50662         if(this.split){
50663             var sh = this.split.el.getHeight();
50664             box.height += sh;
50665             box.y -= sh;
50666         }
50667         return box;
50668     },
50669     
50670     updateBox : function(box){
50671         if(this.split && !this.collapsed){
50672             var sh = this.split.el.getHeight();
50673             box.height -= sh;
50674             box.y += sh;
50675             this.split.el.setLeft(box.x);
50676             this.split.el.setTop(box.y-sh);
50677             this.split.el.setWidth(box.width);
50678         }
50679         if(this.collapsed){
50680             this.updateBody(box.width, null);
50681         }
50682         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50683     }
50684 });
50685
50686 Roo.EastLayoutRegion = function(mgr, config){
50687     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
50688     if(this.split){
50689         this.split.placement = Roo.SplitBar.RIGHT;
50690         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50691         this.split.el.addClass("x-layout-split-h");
50692     }
50693     var size = config.initialSize || config.width;
50694     if(typeof size != "undefined"){
50695         this.el.setWidth(size);
50696     }
50697 };
50698 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
50699     orientation: Roo.SplitBar.HORIZONTAL,
50700     getBox : function(){
50701         if(this.collapsed){
50702             return this.collapsedEl.getBox();
50703         }
50704         var box = this.el.getBox();
50705         if(this.split){
50706             var sw = this.split.el.getWidth();
50707             box.width += sw;
50708             box.x -= sw;
50709         }
50710         return box;
50711     },
50712
50713     updateBox : function(box){
50714         if(this.split && !this.collapsed){
50715             var sw = this.split.el.getWidth();
50716             box.width -= sw;
50717             this.split.el.setLeft(box.x);
50718             this.split.el.setTop(box.y);
50719             this.split.el.setHeight(box.height);
50720             box.x += sw;
50721         }
50722         if(this.collapsed){
50723             this.updateBody(null, box.height);
50724         }
50725         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50726     }
50727 });
50728
50729 Roo.WestLayoutRegion = function(mgr, config){
50730     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
50731     if(this.split){
50732         this.split.placement = Roo.SplitBar.LEFT;
50733         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50734         this.split.el.addClass("x-layout-split-h");
50735     }
50736     var size = config.initialSize || config.width;
50737     if(typeof size != "undefined"){
50738         this.el.setWidth(size);
50739     }
50740 };
50741 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
50742     orientation: Roo.SplitBar.HORIZONTAL,
50743     getBox : function(){
50744         if(this.collapsed){
50745             return this.collapsedEl.getBox();
50746         }
50747         var box = this.el.getBox();
50748         if(this.split){
50749             box.width += this.split.el.getWidth();
50750         }
50751         return box;
50752     },
50753     
50754     updateBox : function(box){
50755         if(this.split && !this.collapsed){
50756             var sw = this.split.el.getWidth();
50757             box.width -= sw;
50758             this.split.el.setLeft(box.x+box.width);
50759             this.split.el.setTop(box.y);
50760             this.split.el.setHeight(box.height);
50761         }
50762         if(this.collapsed){
50763             this.updateBody(null, box.height);
50764         }
50765         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50766     }
50767 });
50768 /*
50769  * Based on:
50770  * Ext JS Library 1.1.1
50771  * Copyright(c) 2006-2007, Ext JS, LLC.
50772  *
50773  * Originally Released Under LGPL - original licence link has changed is not relivant.
50774  *
50775  * Fork - LGPL
50776  * <script type="text/javascript">
50777  */
50778  
50779  
50780 /*
50781  * Private internal class for reading and applying state
50782  */
50783 Roo.LayoutStateManager = function(layout){
50784      // default empty state
50785      this.state = {
50786         north: {},
50787         south: {},
50788         east: {},
50789         west: {}       
50790     };
50791 };
50792
50793 Roo.LayoutStateManager.prototype = {
50794     init : function(layout, provider){
50795         this.provider = provider;
50796         var state = provider.get(layout.id+"-layout-state");
50797         if(state){
50798             var wasUpdating = layout.isUpdating();
50799             if(!wasUpdating){
50800                 layout.beginUpdate();
50801             }
50802             for(var key in state){
50803                 if(typeof state[key] != "function"){
50804                     var rstate = state[key];
50805                     var r = layout.getRegion(key);
50806                     if(r && rstate){
50807                         if(rstate.size){
50808                             r.resizeTo(rstate.size);
50809                         }
50810                         if(rstate.collapsed == true){
50811                             r.collapse(true);
50812                         }else{
50813                             r.expand(null, true);
50814                         }
50815                     }
50816                 }
50817             }
50818             if(!wasUpdating){
50819                 layout.endUpdate();
50820             }
50821             this.state = state; 
50822         }
50823         this.layout = layout;
50824         layout.on("regionresized", this.onRegionResized, this);
50825         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50826         layout.on("regionexpanded", this.onRegionExpanded, this);
50827     },
50828     
50829     storeState : function(){
50830         this.provider.set(this.layout.id+"-layout-state", this.state);
50831     },
50832     
50833     onRegionResized : function(region, newSize){
50834         this.state[region.getPosition()].size = newSize;
50835         this.storeState();
50836     },
50837     
50838     onRegionCollapsed : function(region){
50839         this.state[region.getPosition()].collapsed = true;
50840         this.storeState();
50841     },
50842     
50843     onRegionExpanded : function(region){
50844         this.state[region.getPosition()].collapsed = false;
50845         this.storeState();
50846     }
50847 };/*
50848  * Based on:
50849  * Ext JS Library 1.1.1
50850  * Copyright(c) 2006-2007, Ext JS, LLC.
50851  *
50852  * Originally Released Under LGPL - original licence link has changed is not relivant.
50853  *
50854  * Fork - LGPL
50855  * <script type="text/javascript">
50856  */
50857 /**
50858  * @class Roo.ContentPanel
50859  * @extends Roo.util.Observable
50860  * A basic ContentPanel element.
50861  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50862  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50863  * @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
50864  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50865  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50866  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50867  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50868  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50869  * @cfg {String} title          The title for this panel
50870  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50871  * @cfg {String} url            Calls {@link #setUrl} with this value
50872  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50873  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50874  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50875  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50876
50877  * @constructor
50878  * Create a new ContentPanel.
50879  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50880  * @param {String/Object} config A string to set only the title or a config object
50881  * @param {String} content (optional) Set the HTML content for this panel
50882  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50883  */
50884 Roo.ContentPanel = function(el, config, content){
50885     
50886      
50887     /*
50888     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50889         config = el;
50890         el = Roo.id();
50891     }
50892     if (config && config.parentLayout) { 
50893         el = config.parentLayout.el.createChild(); 
50894     }
50895     */
50896     if(el.autoCreate){ // xtype is available if this is called from factory
50897         config = el;
50898         el = Roo.id();
50899     }
50900     this.el = Roo.get(el);
50901     if(!this.el && config && config.autoCreate){
50902         if(typeof config.autoCreate == "object"){
50903             if(!config.autoCreate.id){
50904                 config.autoCreate.id = config.id||el;
50905             }
50906             this.el = Roo.DomHelper.append(document.body,
50907                         config.autoCreate, true);
50908         }else{
50909             this.el = Roo.DomHelper.append(document.body,
50910                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50911         }
50912     }
50913     this.closable = false;
50914     this.loaded = false;
50915     this.active = false;
50916     if(typeof config == "string"){
50917         this.title = config;
50918     }else{
50919         Roo.apply(this, config);
50920     }
50921     
50922     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50923         this.wrapEl = this.el.wrap();
50924         this.toolbar.container = this.el.insertSibling(false, 'before');
50925         this.toolbar = new Roo.Toolbar(this.toolbar);
50926     }
50927     
50928     // xtype created footer. - not sure if will work as we normally have to render first..
50929     if (this.footer && !this.footer.el && this.footer.xtype) {
50930         if (!this.wrapEl) {
50931             this.wrapEl = this.el.wrap();
50932         }
50933     
50934         this.footer.container = this.wrapEl.createChild();
50935          
50936         this.footer = Roo.factory(this.footer, Roo);
50937         
50938     }
50939     
50940     if(this.resizeEl){
50941         this.resizeEl = Roo.get(this.resizeEl, true);
50942     }else{
50943         this.resizeEl = this.el;
50944     }
50945     // handle view.xtype
50946     
50947  
50948     
50949     
50950     this.addEvents({
50951         /**
50952          * @event activate
50953          * Fires when this panel is activated. 
50954          * @param {Roo.ContentPanel} this
50955          */
50956         "activate" : true,
50957         /**
50958          * @event deactivate
50959          * Fires when this panel is activated. 
50960          * @param {Roo.ContentPanel} this
50961          */
50962         "deactivate" : true,
50963
50964         /**
50965          * @event resize
50966          * Fires when this panel is resized if fitToFrame is true.
50967          * @param {Roo.ContentPanel} this
50968          * @param {Number} width The width after any component adjustments
50969          * @param {Number} height The height after any component adjustments
50970          */
50971         "resize" : true,
50972         
50973          /**
50974          * @event render
50975          * Fires when this tab is created
50976          * @param {Roo.ContentPanel} this
50977          */
50978         "render" : true
50979         
50980         
50981         
50982     });
50983     
50984
50985     
50986     
50987     if(this.autoScroll){
50988         this.resizeEl.setStyle("overflow", "auto");
50989     } else {
50990         // fix randome scrolling
50991         this.el.on('scroll', function() {
50992             Roo.log('fix random scolling');
50993             this.scrollTo('top',0); 
50994         });
50995     }
50996     content = content || this.content;
50997     if(content){
50998         this.setContent(content);
50999     }
51000     if(config && config.url){
51001         this.setUrl(this.url, this.params, this.loadOnce);
51002     }
51003     
51004     
51005     
51006     Roo.ContentPanel.superclass.constructor.call(this);
51007     
51008     if (this.view && typeof(this.view.xtype) != 'undefined') {
51009         this.view.el = this.el.appendChild(document.createElement("div"));
51010         this.view = Roo.factory(this.view); 
51011         this.view.render  &&  this.view.render(false, '');  
51012     }
51013     
51014     
51015     this.fireEvent('render', this);
51016 };
51017
51018 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51019     tabTip:'',
51020     setRegion : function(region){
51021         this.region = region;
51022         if(region){
51023            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51024         }else{
51025            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51026         } 
51027     },
51028     
51029     /**
51030      * Returns the toolbar for this Panel if one was configured. 
51031      * @return {Roo.Toolbar} 
51032      */
51033     getToolbar : function(){
51034         return this.toolbar;
51035     },
51036     
51037     setActiveState : function(active){
51038         this.active = active;
51039         if(!active){
51040             this.fireEvent("deactivate", this);
51041         }else{
51042             this.fireEvent("activate", this);
51043         }
51044     },
51045     /**
51046      * Updates this panel's element
51047      * @param {String} content The new content
51048      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51049     */
51050     setContent : function(content, loadScripts){
51051         this.el.update(content, loadScripts);
51052     },
51053
51054     ignoreResize : function(w, h){
51055         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51056             return true;
51057         }else{
51058             this.lastSize = {width: w, height: h};
51059             return false;
51060         }
51061     },
51062     /**
51063      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51064      * @return {Roo.UpdateManager} The UpdateManager
51065      */
51066     getUpdateManager : function(){
51067         return this.el.getUpdateManager();
51068     },
51069      /**
51070      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51071      * @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:
51072 <pre><code>
51073 panel.load({
51074     url: "your-url.php",
51075     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51076     callback: yourFunction,
51077     scope: yourObject, //(optional scope)
51078     discardUrl: false,
51079     nocache: false,
51080     text: "Loading...",
51081     timeout: 30,
51082     scripts: false
51083 });
51084 </code></pre>
51085      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51086      * 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.
51087      * @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}
51088      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51089      * @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.
51090      * @return {Roo.ContentPanel} this
51091      */
51092     load : function(){
51093         var um = this.el.getUpdateManager();
51094         um.update.apply(um, arguments);
51095         return this;
51096     },
51097
51098
51099     /**
51100      * 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.
51101      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51102      * @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)
51103      * @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)
51104      * @return {Roo.UpdateManager} The UpdateManager
51105      */
51106     setUrl : function(url, params, loadOnce){
51107         if(this.refreshDelegate){
51108             this.removeListener("activate", this.refreshDelegate);
51109         }
51110         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51111         this.on("activate", this.refreshDelegate);
51112         return this.el.getUpdateManager();
51113     },
51114     
51115     _handleRefresh : function(url, params, loadOnce){
51116         if(!loadOnce || !this.loaded){
51117             var updater = this.el.getUpdateManager();
51118             updater.update(url, params, this._setLoaded.createDelegate(this));
51119         }
51120     },
51121     
51122     _setLoaded : function(){
51123         this.loaded = true;
51124     }, 
51125     
51126     /**
51127      * Returns this panel's id
51128      * @return {String} 
51129      */
51130     getId : function(){
51131         return this.el.id;
51132     },
51133     
51134     /** 
51135      * Returns this panel's element - used by regiosn to add.
51136      * @return {Roo.Element} 
51137      */
51138     getEl : function(){
51139         return this.wrapEl || this.el;
51140     },
51141     
51142     adjustForComponents : function(width, height)
51143     {
51144         //Roo.log('adjustForComponents ');
51145         if(this.resizeEl != this.el){
51146             width -= this.el.getFrameWidth('lr');
51147             height -= this.el.getFrameWidth('tb');
51148         }
51149         if(this.toolbar){
51150             var te = this.toolbar.getEl();
51151             height -= te.getHeight();
51152             te.setWidth(width);
51153         }
51154         if(this.footer){
51155             var te = this.footer.getEl();
51156             Roo.log("footer:" + te.getHeight());
51157             
51158             height -= te.getHeight();
51159             te.setWidth(width);
51160         }
51161         
51162         
51163         if(this.adjustments){
51164             width += this.adjustments[0];
51165             height += this.adjustments[1];
51166         }
51167         return {"width": width, "height": height};
51168     },
51169     
51170     setSize : function(width, height){
51171         if(this.fitToFrame && !this.ignoreResize(width, height)){
51172             if(this.fitContainer && this.resizeEl != this.el){
51173                 this.el.setSize(width, height);
51174             }
51175             var size = this.adjustForComponents(width, height);
51176             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51177             this.fireEvent('resize', this, size.width, size.height);
51178         }
51179     },
51180     
51181     /**
51182      * Returns this panel's title
51183      * @return {String} 
51184      */
51185     getTitle : function(){
51186         return this.title;
51187     },
51188     
51189     /**
51190      * Set this panel's title
51191      * @param {String} title
51192      */
51193     setTitle : function(title){
51194         this.title = title;
51195         if(this.region){
51196             this.region.updatePanelTitle(this, title);
51197         }
51198     },
51199     
51200     /**
51201      * Returns true is this panel was configured to be closable
51202      * @return {Boolean} 
51203      */
51204     isClosable : function(){
51205         return this.closable;
51206     },
51207     
51208     beforeSlide : function(){
51209         this.el.clip();
51210         this.resizeEl.clip();
51211     },
51212     
51213     afterSlide : function(){
51214         this.el.unclip();
51215         this.resizeEl.unclip();
51216     },
51217     
51218     /**
51219      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51220      *   Will fail silently if the {@link #setUrl} method has not been called.
51221      *   This does not activate the panel, just updates its content.
51222      */
51223     refresh : function(){
51224         if(this.refreshDelegate){
51225            this.loaded = false;
51226            this.refreshDelegate();
51227         }
51228     },
51229     
51230     /**
51231      * Destroys this panel
51232      */
51233     destroy : function(){
51234         this.el.removeAllListeners();
51235         var tempEl = document.createElement("span");
51236         tempEl.appendChild(this.el.dom);
51237         tempEl.innerHTML = "";
51238         this.el.remove();
51239         this.el = null;
51240     },
51241     
51242     /**
51243      * form - if the content panel contains a form - this is a reference to it.
51244      * @type {Roo.form.Form}
51245      */
51246     form : false,
51247     /**
51248      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51249      *    This contains a reference to it.
51250      * @type {Roo.View}
51251      */
51252     view : false,
51253     
51254       /**
51255      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51256      * <pre><code>
51257
51258 layout.addxtype({
51259        xtype : 'Form',
51260        items: [ .... ]
51261    }
51262 );
51263
51264 </code></pre>
51265      * @param {Object} cfg Xtype definition of item to add.
51266      */
51267     
51268     addxtype : function(cfg) {
51269         // add form..
51270         if (cfg.xtype.match(/^Form$/)) {
51271             
51272             var el;
51273             //if (this.footer) {
51274             //    el = this.footer.container.insertSibling(false, 'before');
51275             //} else {
51276                 el = this.el.createChild();
51277             //}
51278
51279             this.form = new  Roo.form.Form(cfg);
51280             
51281             
51282             if ( this.form.allItems.length) this.form.render(el.dom);
51283             return this.form;
51284         }
51285         // should only have one of theses..
51286         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51287             // views.. should not be just added - used named prop 'view''
51288             
51289             cfg.el = this.el.appendChild(document.createElement("div"));
51290             // factory?
51291             
51292             var ret = new Roo.factory(cfg);
51293              
51294              ret.render && ret.render(false, ''); // render blank..
51295             this.view = ret;
51296             return ret;
51297         }
51298         return false;
51299     }
51300 });
51301
51302 /**
51303  * @class Roo.GridPanel
51304  * @extends Roo.ContentPanel
51305  * @constructor
51306  * Create a new GridPanel.
51307  * @param {Roo.grid.Grid} grid The grid for this panel
51308  * @param {String/Object} config A string to set only the panel's title, or a config object
51309  */
51310 Roo.GridPanel = function(grid, config){
51311     
51312   
51313     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51314         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51315         
51316     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51317     
51318     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51319     
51320     if(this.toolbar){
51321         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51322     }
51323     // xtype created footer. - not sure if will work as we normally have to render first..
51324     if (this.footer && !this.footer.el && this.footer.xtype) {
51325         
51326         this.footer.container = this.grid.getView().getFooterPanel(true);
51327         this.footer.dataSource = this.grid.dataSource;
51328         this.footer = Roo.factory(this.footer, Roo);
51329         
51330     }
51331     
51332     grid.monitorWindowResize = false; // turn off autosizing
51333     grid.autoHeight = false;
51334     grid.autoWidth = false;
51335     this.grid = grid;
51336     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51337 };
51338
51339 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51340     getId : function(){
51341         return this.grid.id;
51342     },
51343     
51344     /**
51345      * Returns the grid for this panel
51346      * @return {Roo.grid.Grid} 
51347      */
51348     getGrid : function(){
51349         return this.grid;    
51350     },
51351     
51352     setSize : function(width, height){
51353         if(!this.ignoreResize(width, height)){
51354             var grid = this.grid;
51355             var size = this.adjustForComponents(width, height);
51356             grid.getGridEl().setSize(size.width, size.height);
51357             grid.autoSize();
51358         }
51359     },
51360     
51361     beforeSlide : function(){
51362         this.grid.getView().scroller.clip();
51363     },
51364     
51365     afterSlide : function(){
51366         this.grid.getView().scroller.unclip();
51367     },
51368     
51369     destroy : function(){
51370         this.grid.destroy();
51371         delete this.grid;
51372         Roo.GridPanel.superclass.destroy.call(this); 
51373     }
51374 });
51375
51376
51377 /**
51378  * @class Roo.NestedLayoutPanel
51379  * @extends Roo.ContentPanel
51380  * @constructor
51381  * Create a new NestedLayoutPanel.
51382  * 
51383  * 
51384  * @param {Roo.BorderLayout} layout The layout for this panel
51385  * @param {String/Object} config A string to set only the title or a config object
51386  */
51387 Roo.NestedLayoutPanel = function(layout, config)
51388 {
51389     // construct with only one argument..
51390     /* FIXME - implement nicer consturctors
51391     if (layout.layout) {
51392         config = layout;
51393         layout = config.layout;
51394         delete config.layout;
51395     }
51396     if (layout.xtype && !layout.getEl) {
51397         // then layout needs constructing..
51398         layout = Roo.factory(layout, Roo);
51399     }
51400     */
51401     
51402     
51403     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51404     
51405     layout.monitorWindowResize = false; // turn off autosizing
51406     this.layout = layout;
51407     this.layout.getEl().addClass("x-layout-nested-layout");
51408     
51409     
51410     
51411     
51412 };
51413
51414 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51415
51416     setSize : function(width, height){
51417         if(!this.ignoreResize(width, height)){
51418             var size = this.adjustForComponents(width, height);
51419             var el = this.layout.getEl();
51420             el.setSize(size.width, size.height);
51421             var touch = el.dom.offsetWidth;
51422             this.layout.layout();
51423             // ie requires a double layout on the first pass
51424             if(Roo.isIE && !this.initialized){
51425                 this.initialized = true;
51426                 this.layout.layout();
51427             }
51428         }
51429     },
51430     
51431     // activate all subpanels if not currently active..
51432     
51433     setActiveState : function(active){
51434         this.active = active;
51435         if(!active){
51436             this.fireEvent("deactivate", this);
51437             return;
51438         }
51439         
51440         this.fireEvent("activate", this);
51441         // not sure if this should happen before or after..
51442         if (!this.layout) {
51443             return; // should not happen..
51444         }
51445         var reg = false;
51446         for (var r in this.layout.regions) {
51447             reg = this.layout.getRegion(r);
51448             if (reg.getActivePanel()) {
51449                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51450                 reg.setActivePanel(reg.getActivePanel());
51451                 continue;
51452             }
51453             if (!reg.panels.length) {
51454                 continue;
51455             }
51456             reg.showPanel(reg.getPanel(0));
51457         }
51458         
51459         
51460         
51461         
51462     },
51463     
51464     /**
51465      * Returns the nested BorderLayout for this panel
51466      * @return {Roo.BorderLayout} 
51467      */
51468     getLayout : function(){
51469         return this.layout;
51470     },
51471     
51472      /**
51473      * Adds a xtype elements to the layout of the nested panel
51474      * <pre><code>
51475
51476 panel.addxtype({
51477        xtype : 'ContentPanel',
51478        region: 'west',
51479        items: [ .... ]
51480    }
51481 );
51482
51483 panel.addxtype({
51484         xtype : 'NestedLayoutPanel',
51485         region: 'west',
51486         layout: {
51487            center: { },
51488            west: { }   
51489         },
51490         items : [ ... list of content panels or nested layout panels.. ]
51491    }
51492 );
51493 </code></pre>
51494      * @param {Object} cfg Xtype definition of item to add.
51495      */
51496     addxtype : function(cfg) {
51497         return this.layout.addxtype(cfg);
51498     
51499     }
51500 });
51501
51502 Roo.ScrollPanel = function(el, config, content){
51503     config = config || {};
51504     config.fitToFrame = true;
51505     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51506     
51507     this.el.dom.style.overflow = "hidden";
51508     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51509     this.el.removeClass("x-layout-inactive-content");
51510     this.el.on("mousewheel", this.onWheel, this);
51511
51512     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51513     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51514     up.unselectable(); down.unselectable();
51515     up.on("click", this.scrollUp, this);
51516     down.on("click", this.scrollDown, this);
51517     up.addClassOnOver("x-scroller-btn-over");
51518     down.addClassOnOver("x-scroller-btn-over");
51519     up.addClassOnClick("x-scroller-btn-click");
51520     down.addClassOnClick("x-scroller-btn-click");
51521     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51522
51523     this.resizeEl = this.el;
51524     this.el = wrap; this.up = up; this.down = down;
51525 };
51526
51527 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51528     increment : 100,
51529     wheelIncrement : 5,
51530     scrollUp : function(){
51531         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51532     },
51533
51534     scrollDown : function(){
51535         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51536     },
51537
51538     afterScroll : function(){
51539         var el = this.resizeEl;
51540         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51541         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51542         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51543     },
51544
51545     setSize : function(){
51546         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
51547         this.afterScroll();
51548     },
51549
51550     onWheel : function(e){
51551         var d = e.getWheelDelta();
51552         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
51553         this.afterScroll();
51554         e.stopEvent();
51555     },
51556
51557     setContent : function(content, loadScripts){
51558         this.resizeEl.update(content, loadScripts);
51559     }
51560
51561 });
51562
51563
51564
51565
51566
51567
51568
51569
51570
51571 /**
51572  * @class Roo.TreePanel
51573  * @extends Roo.ContentPanel
51574  * @constructor
51575  * Create a new TreePanel. - defaults to fit/scoll contents.
51576  * @param {String/Object} config A string to set only the panel's title, or a config object
51577  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
51578  */
51579 Roo.TreePanel = function(config){
51580     var el = config.el;
51581     var tree = config.tree;
51582     delete config.tree; 
51583     delete config.el; // hopefull!
51584     
51585     // wrapper for IE7 strict & safari scroll issue
51586     
51587     var treeEl = el.createChild();
51588     config.resizeEl = treeEl;
51589     
51590     
51591     
51592     Roo.TreePanel.superclass.constructor.call(this, el, config);
51593  
51594  
51595     this.tree = new Roo.tree.TreePanel(treeEl , tree);
51596     //console.log(tree);
51597     this.on('activate', function()
51598     {
51599         if (this.tree.rendered) {
51600             return;
51601         }
51602         //console.log('render tree');
51603         this.tree.render();
51604     });
51605     // this should not be needed.. - it's actually the 'el' that resizes?
51606     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
51607     
51608     //this.on('resize',  function (cp, w, h) {
51609     //        this.tree.innerCt.setWidth(w);
51610     //        this.tree.innerCt.setHeight(h);
51611     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
51612     //});
51613
51614         
51615     
51616 };
51617
51618 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
51619     fitToFrame : true,
51620     autoScroll : true
51621 });
51622
51623
51624
51625
51626
51627
51628
51629
51630
51631
51632
51633 /*
51634  * Based on:
51635  * Ext JS Library 1.1.1
51636  * Copyright(c) 2006-2007, Ext JS, LLC.
51637  *
51638  * Originally Released Under LGPL - original licence link has changed is not relivant.
51639  *
51640  * Fork - LGPL
51641  * <script type="text/javascript">
51642  */
51643  
51644
51645 /**
51646  * @class Roo.ReaderLayout
51647  * @extends Roo.BorderLayout
51648  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
51649  * center region containing two nested regions (a top one for a list view and one for item preview below),
51650  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
51651  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
51652  * expedites the setup of the overall layout and regions for this common application style.
51653  * Example:
51654  <pre><code>
51655 var reader = new Roo.ReaderLayout();
51656 var CP = Roo.ContentPanel;  // shortcut for adding
51657
51658 reader.beginUpdate();
51659 reader.add("north", new CP("north", "North"));
51660 reader.add("west", new CP("west", {title: "West"}));
51661 reader.add("east", new CP("east", {title: "East"}));
51662
51663 reader.regions.listView.add(new CP("listView", "List"));
51664 reader.regions.preview.add(new CP("preview", "Preview"));
51665 reader.endUpdate();
51666 </code></pre>
51667 * @constructor
51668 * Create a new ReaderLayout
51669 * @param {Object} config Configuration options
51670 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
51671 * document.body if omitted)
51672 */
51673 Roo.ReaderLayout = function(config, renderTo){
51674     var c = config || {size:{}};
51675     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
51676         north: c.north !== false ? Roo.apply({
51677             split:false,
51678             initialSize: 32,
51679             titlebar: false
51680         }, c.north) : false,
51681         west: c.west !== false ? Roo.apply({
51682             split:true,
51683             initialSize: 200,
51684             minSize: 175,
51685             maxSize: 400,
51686             titlebar: true,
51687             collapsible: true,
51688             animate: true,
51689             margins:{left:5,right:0,bottom:5,top:5},
51690             cmargins:{left:5,right:5,bottom:5,top:5}
51691         }, c.west) : false,
51692         east: c.east !== false ? Roo.apply({
51693             split:true,
51694             initialSize: 200,
51695             minSize: 175,
51696             maxSize: 400,
51697             titlebar: true,
51698             collapsible: true,
51699             animate: true,
51700             margins:{left:0,right:5,bottom:5,top:5},
51701             cmargins:{left:5,right:5,bottom:5,top:5}
51702         }, c.east) : false,
51703         center: Roo.apply({
51704             tabPosition: 'top',
51705             autoScroll:false,
51706             closeOnTab: true,
51707             titlebar:false,
51708             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
51709         }, c.center)
51710     });
51711
51712     this.el.addClass('x-reader');
51713
51714     this.beginUpdate();
51715
51716     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
51717         south: c.preview !== false ? Roo.apply({
51718             split:true,
51719             initialSize: 200,
51720             minSize: 100,
51721             autoScroll:true,
51722             collapsible:true,
51723             titlebar: true,
51724             cmargins:{top:5,left:0, right:0, bottom:0}
51725         }, c.preview) : false,
51726         center: Roo.apply({
51727             autoScroll:false,
51728             titlebar:false,
51729             minHeight:200
51730         }, c.listView)
51731     });
51732     this.add('center', new Roo.NestedLayoutPanel(inner,
51733             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
51734
51735     this.endUpdate();
51736
51737     this.regions.preview = inner.getRegion('south');
51738     this.regions.listView = inner.getRegion('center');
51739 };
51740
51741 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
51742  * Based on:
51743  * Ext JS Library 1.1.1
51744  * Copyright(c) 2006-2007, Ext JS, LLC.
51745  *
51746  * Originally Released Under LGPL - original licence link has changed is not relivant.
51747  *
51748  * Fork - LGPL
51749  * <script type="text/javascript">
51750  */
51751  
51752 /**
51753  * @class Roo.grid.Grid
51754  * @extends Roo.util.Observable
51755  * This class represents the primary interface of a component based grid control.
51756  * <br><br>Usage:<pre><code>
51757  var grid = new Roo.grid.Grid("my-container-id", {
51758      ds: myDataStore,
51759      cm: myColModel,
51760      selModel: mySelectionModel,
51761      autoSizeColumns: true,
51762      monitorWindowResize: false,
51763      trackMouseOver: true
51764  });
51765  // set any options
51766  grid.render();
51767  * </code></pre>
51768  * <b>Common Problems:</b><br/>
51769  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51770  * element will correct this<br/>
51771  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51772  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51773  * are unpredictable.<br/>
51774  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51775  * grid to calculate dimensions/offsets.<br/>
51776   * @constructor
51777  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51778  * The container MUST have some type of size defined for the grid to fill. The container will be
51779  * automatically set to position relative if it isn't already.
51780  * @param {Object} config A config object that sets properties on this grid.
51781  */
51782 Roo.grid.Grid = function(container, config){
51783         // initialize the container
51784         this.container = Roo.get(container);
51785         this.container.update("");
51786         this.container.setStyle("overflow", "hidden");
51787     this.container.addClass('x-grid-container');
51788
51789     this.id = this.container.id;
51790
51791     Roo.apply(this, config);
51792     // check and correct shorthanded configs
51793     if(this.ds){
51794         this.dataSource = this.ds;
51795         delete this.ds;
51796     }
51797     if(this.cm){
51798         this.colModel = this.cm;
51799         delete this.cm;
51800     }
51801     if(this.sm){
51802         this.selModel = this.sm;
51803         delete this.sm;
51804     }
51805
51806     if (this.selModel) {
51807         this.selModel = Roo.factory(this.selModel, Roo.grid);
51808         this.sm = this.selModel;
51809         this.sm.xmodule = this.xmodule || false;
51810     }
51811     if (typeof(this.colModel.config) == 'undefined') {
51812         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51813         this.cm = this.colModel;
51814         this.cm.xmodule = this.xmodule || false;
51815     }
51816     if (this.dataSource) {
51817         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51818         this.ds = this.dataSource;
51819         this.ds.xmodule = this.xmodule || false;
51820          
51821     }
51822     
51823     
51824     
51825     if(this.width){
51826         this.container.setWidth(this.width);
51827     }
51828
51829     if(this.height){
51830         this.container.setHeight(this.height);
51831     }
51832     /** @private */
51833         this.addEvents({
51834         // raw events
51835         /**
51836          * @event click
51837          * The raw click event for the entire grid.
51838          * @param {Roo.EventObject} e
51839          */
51840         "click" : true,
51841         /**
51842          * @event dblclick
51843          * The raw dblclick event for the entire grid.
51844          * @param {Roo.EventObject} e
51845          */
51846         "dblclick" : true,
51847         /**
51848          * @event contextmenu
51849          * The raw contextmenu event for the entire grid.
51850          * @param {Roo.EventObject} e
51851          */
51852         "contextmenu" : true,
51853         /**
51854          * @event mousedown
51855          * The raw mousedown event for the entire grid.
51856          * @param {Roo.EventObject} e
51857          */
51858         "mousedown" : true,
51859         /**
51860          * @event mouseup
51861          * The raw mouseup event for the entire grid.
51862          * @param {Roo.EventObject} e
51863          */
51864         "mouseup" : true,
51865         /**
51866          * @event mouseover
51867          * The raw mouseover event for the entire grid.
51868          * @param {Roo.EventObject} e
51869          */
51870         "mouseover" : true,
51871         /**
51872          * @event mouseout
51873          * The raw mouseout event for the entire grid.
51874          * @param {Roo.EventObject} e
51875          */
51876         "mouseout" : true,
51877         /**
51878          * @event keypress
51879          * The raw keypress event for the entire grid.
51880          * @param {Roo.EventObject} e
51881          */
51882         "keypress" : true,
51883         /**
51884          * @event keydown
51885          * The raw keydown event for the entire grid.
51886          * @param {Roo.EventObject} e
51887          */
51888         "keydown" : true,
51889
51890         // custom events
51891
51892         /**
51893          * @event cellclick
51894          * Fires when a cell is clicked
51895          * @param {Grid} this
51896          * @param {Number} rowIndex
51897          * @param {Number} columnIndex
51898          * @param {Roo.EventObject} e
51899          */
51900         "cellclick" : true,
51901         /**
51902          * @event celldblclick
51903          * Fires when a cell is double clicked
51904          * @param {Grid} this
51905          * @param {Number} rowIndex
51906          * @param {Number} columnIndex
51907          * @param {Roo.EventObject} e
51908          */
51909         "celldblclick" : true,
51910         /**
51911          * @event rowclick
51912          * Fires when a row is clicked
51913          * @param {Grid} this
51914          * @param {Number} rowIndex
51915          * @param {Roo.EventObject} e
51916          */
51917         "rowclick" : true,
51918         /**
51919          * @event rowdblclick
51920          * Fires when a row is double clicked
51921          * @param {Grid} this
51922          * @param {Number} rowIndex
51923          * @param {Roo.EventObject} e
51924          */
51925         "rowdblclick" : true,
51926         /**
51927          * @event headerclick
51928          * Fires when a header is clicked
51929          * @param {Grid} this
51930          * @param {Number} columnIndex
51931          * @param {Roo.EventObject} e
51932          */
51933         "headerclick" : true,
51934         /**
51935          * @event headerdblclick
51936          * Fires when a header cell is double clicked
51937          * @param {Grid} this
51938          * @param {Number} columnIndex
51939          * @param {Roo.EventObject} e
51940          */
51941         "headerdblclick" : true,
51942         /**
51943          * @event rowcontextmenu
51944          * Fires when a row is right clicked
51945          * @param {Grid} this
51946          * @param {Number} rowIndex
51947          * @param {Roo.EventObject} e
51948          */
51949         "rowcontextmenu" : true,
51950         /**
51951          * @event cellcontextmenu
51952          * Fires when a cell is right clicked
51953          * @param {Grid} this
51954          * @param {Number} rowIndex
51955          * @param {Number} cellIndex
51956          * @param {Roo.EventObject} e
51957          */
51958          "cellcontextmenu" : true,
51959         /**
51960          * @event headercontextmenu
51961          * Fires when a header is right clicked
51962          * @param {Grid} this
51963          * @param {Number} columnIndex
51964          * @param {Roo.EventObject} e
51965          */
51966         "headercontextmenu" : true,
51967         /**
51968          * @event bodyscroll
51969          * Fires when the body element is scrolled
51970          * @param {Number} scrollLeft
51971          * @param {Number} scrollTop
51972          */
51973         "bodyscroll" : true,
51974         /**
51975          * @event columnresize
51976          * Fires when the user resizes a column
51977          * @param {Number} columnIndex
51978          * @param {Number} newSize
51979          */
51980         "columnresize" : true,
51981         /**
51982          * @event columnmove
51983          * Fires when the user moves a column
51984          * @param {Number} oldIndex
51985          * @param {Number} newIndex
51986          */
51987         "columnmove" : true,
51988         /**
51989          * @event startdrag
51990          * Fires when row(s) start being dragged
51991          * @param {Grid} this
51992          * @param {Roo.GridDD} dd The drag drop object
51993          * @param {event} e The raw browser event
51994          */
51995         "startdrag" : true,
51996         /**
51997          * @event enddrag
51998          * Fires when a drag operation is complete
51999          * @param {Grid} this
52000          * @param {Roo.GridDD} dd The drag drop object
52001          * @param {event} e The raw browser event
52002          */
52003         "enddrag" : true,
52004         /**
52005          * @event dragdrop
52006          * Fires when dragged row(s) are dropped on a valid DD target
52007          * @param {Grid} this
52008          * @param {Roo.GridDD} dd The drag drop object
52009          * @param {String} targetId The target drag drop object
52010          * @param {event} e The raw browser event
52011          */
52012         "dragdrop" : true,
52013         /**
52014          * @event dragover
52015          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52016          * @param {Grid} this
52017          * @param {Roo.GridDD} dd The drag drop object
52018          * @param {String} targetId The target drag drop object
52019          * @param {event} e The raw browser event
52020          */
52021         "dragover" : true,
52022         /**
52023          * @event dragenter
52024          *  Fires when the dragged row(s) first cross another DD target while being dragged
52025          * @param {Grid} this
52026          * @param {Roo.GridDD} dd The drag drop object
52027          * @param {String} targetId The target drag drop object
52028          * @param {event} e The raw browser event
52029          */
52030         "dragenter" : true,
52031         /**
52032          * @event dragout
52033          * Fires when the dragged row(s) leave another DD target while being dragged
52034          * @param {Grid} this
52035          * @param {Roo.GridDD} dd The drag drop object
52036          * @param {String} targetId The target drag drop object
52037          * @param {event} e The raw browser event
52038          */
52039         "dragout" : true,
52040         /**
52041          * @event rowclass
52042          * Fires when a row is rendered, so you can change add a style to it.
52043          * @param {GridView} gridview   The grid view
52044          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52045          */
52046         'rowclass' : true,
52047
52048         /**
52049          * @event render
52050          * Fires when the grid is rendered
52051          * @param {Grid} grid
52052          */
52053         'render' : true
52054     });
52055
52056     Roo.grid.Grid.superclass.constructor.call(this);
52057 };
52058 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52059     
52060     /**
52061      * @cfg {String} ddGroup - drag drop group.
52062      */
52063
52064     /**
52065      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52066      */
52067     minColumnWidth : 25,
52068
52069     /**
52070      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52071      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52072      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52073      */
52074     autoSizeColumns : false,
52075
52076     /**
52077      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52078      */
52079     autoSizeHeaders : true,
52080
52081     /**
52082      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52083      */
52084     monitorWindowResize : true,
52085
52086     /**
52087      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52088      * rows measured to get a columns size. Default is 0 (all rows).
52089      */
52090     maxRowsToMeasure : 0,
52091
52092     /**
52093      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52094      */
52095     trackMouseOver : true,
52096
52097     /**
52098     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52099     */
52100     
52101     /**
52102     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52103     */
52104     enableDragDrop : false,
52105     
52106     /**
52107     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52108     */
52109     enableColumnMove : true,
52110     
52111     /**
52112     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52113     */
52114     enableColumnHide : true,
52115     
52116     /**
52117     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52118     */
52119     enableRowHeightSync : false,
52120     
52121     /**
52122     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52123     */
52124     stripeRows : true,
52125     
52126     /**
52127     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52128     */
52129     autoHeight : false,
52130
52131     /**
52132      * @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.
52133      */
52134     autoExpandColumn : false,
52135
52136     /**
52137     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52138     * Default is 50.
52139     */
52140     autoExpandMin : 50,
52141
52142     /**
52143     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52144     */
52145     autoExpandMax : 1000,
52146
52147     /**
52148     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52149     */
52150     view : null,
52151
52152     /**
52153     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52154     */
52155     loadMask : false,
52156     /**
52157     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52158     */
52159     dropTarget: false,
52160     
52161    
52162     
52163     // private
52164     rendered : false,
52165
52166     /**
52167     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52168     * of a fixed width. Default is false.
52169     */
52170     /**
52171     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52172     */
52173     /**
52174      * Called once after all setup has been completed and the grid is ready to be rendered.
52175      * @return {Roo.grid.Grid} this
52176      */
52177     render : function()
52178     {
52179         var c = this.container;
52180         // try to detect autoHeight/width mode
52181         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52182             this.autoHeight = true;
52183         }
52184         var view = this.getView();
52185         view.init(this);
52186
52187         c.on("click", this.onClick, this);
52188         c.on("dblclick", this.onDblClick, this);
52189         c.on("contextmenu", this.onContextMenu, this);
52190         c.on("keydown", this.onKeyDown, this);
52191         if (Roo.isTouch) {
52192             c.on("touchstart", this.onTouchStart, this);
52193         }
52194
52195         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52196
52197         this.getSelectionModel().init(this);
52198
52199         view.render();
52200
52201         if(this.loadMask){
52202             this.loadMask = new Roo.LoadMask(this.container,
52203                     Roo.apply({store:this.dataSource}, this.loadMask));
52204         }
52205         
52206         
52207         if (this.toolbar && this.toolbar.xtype) {
52208             this.toolbar.container = this.getView().getHeaderPanel(true);
52209             this.toolbar = new Roo.Toolbar(this.toolbar);
52210         }
52211         if (this.footer && this.footer.xtype) {
52212             this.footer.dataSource = this.getDataSource();
52213             this.footer.container = this.getView().getFooterPanel(true);
52214             this.footer = Roo.factory(this.footer, Roo);
52215         }
52216         if (this.dropTarget && this.dropTarget.xtype) {
52217             delete this.dropTarget.xtype;
52218             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52219         }
52220         
52221         
52222         this.rendered = true;
52223         this.fireEvent('render', this);
52224         return this;
52225     },
52226
52227         /**
52228          * Reconfigures the grid to use a different Store and Column Model.
52229          * The View will be bound to the new objects and refreshed.
52230          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52231          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52232          */
52233     reconfigure : function(dataSource, colModel){
52234         if(this.loadMask){
52235             this.loadMask.destroy();
52236             this.loadMask = new Roo.LoadMask(this.container,
52237                     Roo.apply({store:dataSource}, this.loadMask));
52238         }
52239         this.view.bind(dataSource, colModel);
52240         this.dataSource = dataSource;
52241         this.colModel = colModel;
52242         this.view.refresh(true);
52243     },
52244
52245     // private
52246     onKeyDown : function(e){
52247         this.fireEvent("keydown", e);
52248     },
52249
52250     /**
52251      * Destroy this grid.
52252      * @param {Boolean} removeEl True to remove the element
52253      */
52254     destroy : function(removeEl, keepListeners){
52255         if(this.loadMask){
52256             this.loadMask.destroy();
52257         }
52258         var c = this.container;
52259         c.removeAllListeners();
52260         this.view.destroy();
52261         this.colModel.purgeListeners();
52262         if(!keepListeners){
52263             this.purgeListeners();
52264         }
52265         c.update("");
52266         if(removeEl === true){
52267             c.remove();
52268         }
52269     },
52270
52271     // private
52272     processEvent : function(name, e){
52273         // does this fire select???
52274         Roo.log('grid:processEvent '  + name);
52275         
52276         if (name != 'touchstart' ) {
52277             this.fireEvent(name, e);    
52278         }
52279         
52280         var t = e.getTarget();
52281         var v = this.view;
52282         var header = v.findHeaderIndex(t);
52283         if(header !== false){
52284             var ename = name == 'touchstart' ? 'click' : name;
52285              
52286             this.fireEvent("header" + ename, this, header, e);
52287         }else{
52288             var row = v.findRowIndex(t);
52289             var cell = v.findCellIndex(t);
52290             if (name == 'touchstart') {
52291                 // first touch is always a click.
52292                 // hopefull this happens after selection is updated.?
52293                 name = false;
52294                 
52295                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52296                     var cs = this.selModel.getSelectedCell();
52297                     if (row == cs[0] && cell == cs[1]){
52298                         name = 'dblclick';
52299                     }
52300                 }
52301                 if (typeof(this.selModel.getSelections) != 'undefined') {
52302                     var cs = this.selModel.getSelections();
52303                     var ds = this.dataSource;
52304                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52305                         name = 'dblclick';
52306                     }
52307                 }
52308                 if (!name) {
52309                     return;
52310                 }
52311             }
52312             
52313             
52314             if(row !== false){
52315                 this.fireEvent("row" + name, this, row, e);
52316                 if(cell !== false){
52317                     this.fireEvent("cell" + name, this, row, cell, e);
52318                 }
52319             }
52320         }
52321     },
52322
52323     // private
52324     onClick : function(e){
52325         this.processEvent("click", e);
52326     },
52327    // private
52328     onTouchStart : function(e){
52329         this.processEvent("touchstart", e);
52330     },
52331
52332     // private
52333     onContextMenu : function(e, t){
52334         this.processEvent("contextmenu", e);
52335     },
52336
52337     // private
52338     onDblClick : function(e){
52339         this.processEvent("dblclick", e);
52340     },
52341
52342     // private
52343     walkCells : function(row, col, step, fn, scope){
52344         var cm = this.colModel, clen = cm.getColumnCount();
52345         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52346         if(step < 0){
52347             if(col < 0){
52348                 row--;
52349                 first = false;
52350             }
52351             while(row >= 0){
52352                 if(!first){
52353                     col = clen-1;
52354                 }
52355                 first = false;
52356                 while(col >= 0){
52357                     if(fn.call(scope || this, row, col, cm) === true){
52358                         return [row, col];
52359                     }
52360                     col--;
52361                 }
52362                 row--;
52363             }
52364         } else {
52365             if(col >= clen){
52366                 row++;
52367                 first = false;
52368             }
52369             while(row < rlen){
52370                 if(!first){
52371                     col = 0;
52372                 }
52373                 first = false;
52374                 while(col < clen){
52375                     if(fn.call(scope || this, row, col, cm) === true){
52376                         return [row, col];
52377                     }
52378                     col++;
52379                 }
52380                 row++;
52381             }
52382         }
52383         return null;
52384     },
52385
52386     // private
52387     getSelections : function(){
52388         return this.selModel.getSelections();
52389     },
52390
52391     /**
52392      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52393      * but if manual update is required this method will initiate it.
52394      */
52395     autoSize : function(){
52396         if(this.rendered){
52397             this.view.layout();
52398             if(this.view.adjustForScroll){
52399                 this.view.adjustForScroll();
52400             }
52401         }
52402     },
52403
52404     /**
52405      * Returns the grid's underlying element.
52406      * @return {Element} The element
52407      */
52408     getGridEl : function(){
52409         return this.container;
52410     },
52411
52412     // private for compatibility, overridden by editor grid
52413     stopEditing : function(){},
52414
52415     /**
52416      * Returns the grid's SelectionModel.
52417      * @return {SelectionModel}
52418      */
52419     getSelectionModel : function(){
52420         if(!this.selModel){
52421             this.selModel = new Roo.grid.RowSelectionModel();
52422         }
52423         return this.selModel;
52424     },
52425
52426     /**
52427      * Returns the grid's DataSource.
52428      * @return {DataSource}
52429      */
52430     getDataSource : function(){
52431         return this.dataSource;
52432     },
52433
52434     /**
52435      * Returns the grid's ColumnModel.
52436      * @return {ColumnModel}
52437      */
52438     getColumnModel : function(){
52439         return this.colModel;
52440     },
52441
52442     /**
52443      * Returns the grid's GridView object.
52444      * @return {GridView}
52445      */
52446     getView : function(){
52447         if(!this.view){
52448             this.view = new Roo.grid.GridView(this.viewConfig);
52449         }
52450         return this.view;
52451     },
52452     /**
52453      * Called to get grid's drag proxy text, by default returns this.ddText.
52454      * @return {String}
52455      */
52456     getDragDropText : function(){
52457         var count = this.selModel.getCount();
52458         return String.format(this.ddText, count, count == 1 ? '' : 's');
52459     }
52460 });
52461 /**
52462  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52463  * %0 is replaced with the number of selected rows.
52464  * @type String
52465  */
52466 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52467  * Based on:
52468  * Ext JS Library 1.1.1
52469  * Copyright(c) 2006-2007, Ext JS, LLC.
52470  *
52471  * Originally Released Under LGPL - original licence link has changed is not relivant.
52472  *
52473  * Fork - LGPL
52474  * <script type="text/javascript">
52475  */
52476  
52477 Roo.grid.AbstractGridView = function(){
52478         this.grid = null;
52479         
52480         this.events = {
52481             "beforerowremoved" : true,
52482             "beforerowsinserted" : true,
52483             "beforerefresh" : true,
52484             "rowremoved" : true,
52485             "rowsinserted" : true,
52486             "rowupdated" : true,
52487             "refresh" : true
52488         };
52489     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52490 };
52491
52492 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52493     rowClass : "x-grid-row",
52494     cellClass : "x-grid-cell",
52495     tdClass : "x-grid-td",
52496     hdClass : "x-grid-hd",
52497     splitClass : "x-grid-hd-split",
52498     
52499     init: function(grid){
52500         this.grid = grid;
52501                 var cid = this.grid.getGridEl().id;
52502         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52503         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52504         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52505         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52506         },
52507         
52508     getColumnRenderers : function(){
52509         var renderers = [];
52510         var cm = this.grid.colModel;
52511         var colCount = cm.getColumnCount();
52512         for(var i = 0; i < colCount; i++){
52513             renderers[i] = cm.getRenderer(i);
52514         }
52515         return renderers;
52516     },
52517     
52518     getColumnIds : function(){
52519         var ids = [];
52520         var cm = this.grid.colModel;
52521         var colCount = cm.getColumnCount();
52522         for(var i = 0; i < colCount; i++){
52523             ids[i] = cm.getColumnId(i);
52524         }
52525         return ids;
52526     },
52527     
52528     getDataIndexes : function(){
52529         if(!this.indexMap){
52530             this.indexMap = this.buildIndexMap();
52531         }
52532         return this.indexMap.colToData;
52533     },
52534     
52535     getColumnIndexByDataIndex : function(dataIndex){
52536         if(!this.indexMap){
52537             this.indexMap = this.buildIndexMap();
52538         }
52539         return this.indexMap.dataToCol[dataIndex];
52540     },
52541     
52542     /**
52543      * Set a css style for a column dynamically. 
52544      * @param {Number} colIndex The index of the column
52545      * @param {String} name The css property name
52546      * @param {String} value The css value
52547      */
52548     setCSSStyle : function(colIndex, name, value){
52549         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
52550         Roo.util.CSS.updateRule(selector, name, value);
52551     },
52552     
52553     generateRules : function(cm){
52554         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
52555         Roo.util.CSS.removeStyleSheet(rulesId);
52556         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52557             var cid = cm.getColumnId(i);
52558             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
52559                          this.tdSelector, cid, " {\n}\n",
52560                          this.hdSelector, cid, " {\n}\n",
52561                          this.splitSelector, cid, " {\n}\n");
52562         }
52563         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52564     }
52565 });/*
52566  * Based on:
52567  * Ext JS Library 1.1.1
52568  * Copyright(c) 2006-2007, Ext JS, LLC.
52569  *
52570  * Originally Released Under LGPL - original licence link has changed is not relivant.
52571  *
52572  * Fork - LGPL
52573  * <script type="text/javascript">
52574  */
52575
52576 // private
52577 // This is a support class used internally by the Grid components
52578 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
52579     this.grid = grid;
52580     this.view = grid.getView();
52581     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52582     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
52583     if(hd2){
52584         this.setHandleElId(Roo.id(hd));
52585         this.setOuterHandleElId(Roo.id(hd2));
52586     }
52587     this.scroll = false;
52588 };
52589 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
52590     maxDragWidth: 120,
52591     getDragData : function(e){
52592         var t = Roo.lib.Event.getTarget(e);
52593         var h = this.view.findHeaderCell(t);
52594         if(h){
52595             return {ddel: h.firstChild, header:h};
52596         }
52597         return false;
52598     },
52599
52600     onInitDrag : function(e){
52601         this.view.headersDisabled = true;
52602         var clone = this.dragData.ddel.cloneNode(true);
52603         clone.id = Roo.id();
52604         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
52605         this.proxy.update(clone);
52606         return true;
52607     },
52608
52609     afterValidDrop : function(){
52610         var v = this.view;
52611         setTimeout(function(){
52612             v.headersDisabled = false;
52613         }, 50);
52614     },
52615
52616     afterInvalidDrop : function(){
52617         var v = this.view;
52618         setTimeout(function(){
52619             v.headersDisabled = false;
52620         }, 50);
52621     }
52622 });
52623 /*
52624  * Based on:
52625  * Ext JS Library 1.1.1
52626  * Copyright(c) 2006-2007, Ext JS, LLC.
52627  *
52628  * Originally Released Under LGPL - original licence link has changed is not relivant.
52629  *
52630  * Fork - LGPL
52631  * <script type="text/javascript">
52632  */
52633 // private
52634 // This is a support class used internally by the Grid components
52635 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
52636     this.grid = grid;
52637     this.view = grid.getView();
52638     // split the proxies so they don't interfere with mouse events
52639     this.proxyTop = Roo.DomHelper.append(document.body, {
52640         cls:"col-move-top", html:"&#160;"
52641     }, true);
52642     this.proxyBottom = Roo.DomHelper.append(document.body, {
52643         cls:"col-move-bottom", html:"&#160;"
52644     }, true);
52645     this.proxyTop.hide = this.proxyBottom.hide = function(){
52646         this.setLeftTop(-100,-100);
52647         this.setStyle("visibility", "hidden");
52648     };
52649     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52650     // temporarily disabled
52651     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
52652     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
52653 };
52654 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
52655     proxyOffsets : [-4, -9],
52656     fly: Roo.Element.fly,
52657
52658     getTargetFromEvent : function(e){
52659         var t = Roo.lib.Event.getTarget(e);
52660         var cindex = this.view.findCellIndex(t);
52661         if(cindex !== false){
52662             return this.view.getHeaderCell(cindex);
52663         }
52664         return null;
52665     },
52666
52667     nextVisible : function(h){
52668         var v = this.view, cm = this.grid.colModel;
52669         h = h.nextSibling;
52670         while(h){
52671             if(!cm.isHidden(v.getCellIndex(h))){
52672                 return h;
52673             }
52674             h = h.nextSibling;
52675         }
52676         return null;
52677     },
52678
52679     prevVisible : function(h){
52680         var v = this.view, cm = this.grid.colModel;
52681         h = h.prevSibling;
52682         while(h){
52683             if(!cm.isHidden(v.getCellIndex(h))){
52684                 return h;
52685             }
52686             h = h.prevSibling;
52687         }
52688         return null;
52689     },
52690
52691     positionIndicator : function(h, n, e){
52692         var x = Roo.lib.Event.getPageX(e);
52693         var r = Roo.lib.Dom.getRegion(n.firstChild);
52694         var px, pt, py = r.top + this.proxyOffsets[1];
52695         if((r.right - x) <= (r.right-r.left)/2){
52696             px = r.right+this.view.borderWidth;
52697             pt = "after";
52698         }else{
52699             px = r.left;
52700             pt = "before";
52701         }
52702         var oldIndex = this.view.getCellIndex(h);
52703         var newIndex = this.view.getCellIndex(n);
52704
52705         if(this.grid.colModel.isFixed(newIndex)){
52706             return false;
52707         }
52708
52709         var locked = this.grid.colModel.isLocked(newIndex);
52710
52711         if(pt == "after"){
52712             newIndex++;
52713         }
52714         if(oldIndex < newIndex){
52715             newIndex--;
52716         }
52717         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
52718             return false;
52719         }
52720         px +=  this.proxyOffsets[0];
52721         this.proxyTop.setLeftTop(px, py);
52722         this.proxyTop.show();
52723         if(!this.bottomOffset){
52724             this.bottomOffset = this.view.mainHd.getHeight();
52725         }
52726         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
52727         this.proxyBottom.show();
52728         return pt;
52729     },
52730
52731     onNodeEnter : function(n, dd, e, data){
52732         if(data.header != n){
52733             this.positionIndicator(data.header, n, e);
52734         }
52735     },
52736
52737     onNodeOver : function(n, dd, e, data){
52738         var result = false;
52739         if(data.header != n){
52740             result = this.positionIndicator(data.header, n, e);
52741         }
52742         if(!result){
52743             this.proxyTop.hide();
52744             this.proxyBottom.hide();
52745         }
52746         return result ? this.dropAllowed : this.dropNotAllowed;
52747     },
52748
52749     onNodeOut : function(n, dd, e, data){
52750         this.proxyTop.hide();
52751         this.proxyBottom.hide();
52752     },
52753
52754     onNodeDrop : function(n, dd, e, data){
52755         var h = data.header;
52756         if(h != n){
52757             var cm = this.grid.colModel;
52758             var x = Roo.lib.Event.getPageX(e);
52759             var r = Roo.lib.Dom.getRegion(n.firstChild);
52760             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52761             var oldIndex = this.view.getCellIndex(h);
52762             var newIndex = this.view.getCellIndex(n);
52763             var locked = cm.isLocked(newIndex);
52764             if(pt == "after"){
52765                 newIndex++;
52766             }
52767             if(oldIndex < newIndex){
52768                 newIndex--;
52769             }
52770             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52771                 return false;
52772             }
52773             cm.setLocked(oldIndex, locked, true);
52774             cm.moveColumn(oldIndex, newIndex);
52775             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52776             return true;
52777         }
52778         return false;
52779     }
52780 });
52781 /*
52782  * Based on:
52783  * Ext JS Library 1.1.1
52784  * Copyright(c) 2006-2007, Ext JS, LLC.
52785  *
52786  * Originally Released Under LGPL - original licence link has changed is not relivant.
52787  *
52788  * Fork - LGPL
52789  * <script type="text/javascript">
52790  */
52791   
52792 /**
52793  * @class Roo.grid.GridView
52794  * @extends Roo.util.Observable
52795  *
52796  * @constructor
52797  * @param {Object} config
52798  */
52799 Roo.grid.GridView = function(config){
52800     Roo.grid.GridView.superclass.constructor.call(this);
52801     this.el = null;
52802
52803     Roo.apply(this, config);
52804 };
52805
52806 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52807
52808     unselectable :  'unselectable="on"',
52809     unselectableCls :  'x-unselectable',
52810     
52811     
52812     rowClass : "x-grid-row",
52813
52814     cellClass : "x-grid-col",
52815
52816     tdClass : "x-grid-td",
52817
52818     hdClass : "x-grid-hd",
52819
52820     splitClass : "x-grid-split",
52821
52822     sortClasses : ["sort-asc", "sort-desc"],
52823
52824     enableMoveAnim : false,
52825
52826     hlColor: "C3DAF9",
52827
52828     dh : Roo.DomHelper,
52829
52830     fly : Roo.Element.fly,
52831
52832     css : Roo.util.CSS,
52833
52834     borderWidth: 1,
52835
52836     splitOffset: 3,
52837
52838     scrollIncrement : 22,
52839
52840     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52841
52842     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52843
52844     bind : function(ds, cm){
52845         if(this.ds){
52846             this.ds.un("load", this.onLoad, this);
52847             this.ds.un("datachanged", this.onDataChange, this);
52848             this.ds.un("add", this.onAdd, this);
52849             this.ds.un("remove", this.onRemove, this);
52850             this.ds.un("update", this.onUpdate, this);
52851             this.ds.un("clear", this.onClear, this);
52852         }
52853         if(ds){
52854             ds.on("load", this.onLoad, this);
52855             ds.on("datachanged", this.onDataChange, this);
52856             ds.on("add", this.onAdd, this);
52857             ds.on("remove", this.onRemove, this);
52858             ds.on("update", this.onUpdate, this);
52859             ds.on("clear", this.onClear, this);
52860         }
52861         this.ds = ds;
52862
52863         if(this.cm){
52864             this.cm.un("widthchange", this.onColWidthChange, this);
52865             this.cm.un("headerchange", this.onHeaderChange, this);
52866             this.cm.un("hiddenchange", this.onHiddenChange, this);
52867             this.cm.un("columnmoved", this.onColumnMove, this);
52868             this.cm.un("columnlockchange", this.onColumnLock, this);
52869         }
52870         if(cm){
52871             this.generateRules(cm);
52872             cm.on("widthchange", this.onColWidthChange, this);
52873             cm.on("headerchange", this.onHeaderChange, this);
52874             cm.on("hiddenchange", this.onHiddenChange, this);
52875             cm.on("columnmoved", this.onColumnMove, this);
52876             cm.on("columnlockchange", this.onColumnLock, this);
52877         }
52878         this.cm = cm;
52879     },
52880
52881     init: function(grid){
52882         Roo.grid.GridView.superclass.init.call(this, grid);
52883
52884         this.bind(grid.dataSource, grid.colModel);
52885
52886         grid.on("headerclick", this.handleHeaderClick, this);
52887
52888         if(grid.trackMouseOver){
52889             grid.on("mouseover", this.onRowOver, this);
52890             grid.on("mouseout", this.onRowOut, this);
52891         }
52892         grid.cancelTextSelection = function(){};
52893         this.gridId = grid.id;
52894
52895         var tpls = this.templates || {};
52896
52897         if(!tpls.master){
52898             tpls.master = new Roo.Template(
52899                '<div class="x-grid" hidefocus="true">',
52900                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52901                   '<div class="x-grid-topbar"></div>',
52902                   '<div class="x-grid-scroller"><div></div></div>',
52903                   '<div class="x-grid-locked">',
52904                       '<div class="x-grid-header">{lockedHeader}</div>',
52905                       '<div class="x-grid-body">{lockedBody}</div>',
52906                   "</div>",
52907                   '<div class="x-grid-viewport">',
52908                       '<div class="x-grid-header">{header}</div>',
52909                       '<div class="x-grid-body">{body}</div>',
52910                   "</div>",
52911                   '<div class="x-grid-bottombar"></div>',
52912                  
52913                   '<div class="x-grid-resize-proxy">&#160;</div>',
52914                "</div>"
52915             );
52916             tpls.master.disableformats = true;
52917         }
52918
52919         if(!tpls.header){
52920             tpls.header = new Roo.Template(
52921                '<table border="0" cellspacing="0" cellpadding="0">',
52922                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52923                "</table>{splits}"
52924             );
52925             tpls.header.disableformats = true;
52926         }
52927         tpls.header.compile();
52928
52929         if(!tpls.hcell){
52930             tpls.hcell = new Roo.Template(
52931                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52932                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52933                 "</div></td>"
52934              );
52935              tpls.hcell.disableFormats = true;
52936         }
52937         tpls.hcell.compile();
52938
52939         if(!tpls.hsplit){
52940             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52941                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52942             tpls.hsplit.disableFormats = true;
52943         }
52944         tpls.hsplit.compile();
52945
52946         if(!tpls.body){
52947             tpls.body = new Roo.Template(
52948                '<table border="0" cellspacing="0" cellpadding="0">',
52949                "<tbody>{rows}</tbody>",
52950                "</table>"
52951             );
52952             tpls.body.disableFormats = true;
52953         }
52954         tpls.body.compile();
52955
52956         if(!tpls.row){
52957             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52958             tpls.row.disableFormats = true;
52959         }
52960         tpls.row.compile();
52961
52962         if(!tpls.cell){
52963             tpls.cell = new Roo.Template(
52964                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52965                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52966                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52967                 "</td>"
52968             );
52969             tpls.cell.disableFormats = true;
52970         }
52971         tpls.cell.compile();
52972
52973         this.templates = tpls;
52974     },
52975
52976     // remap these for backwards compat
52977     onColWidthChange : function(){
52978         this.updateColumns.apply(this, arguments);
52979     },
52980     onHeaderChange : function(){
52981         this.updateHeaders.apply(this, arguments);
52982     }, 
52983     onHiddenChange : function(){
52984         this.handleHiddenChange.apply(this, arguments);
52985     },
52986     onColumnMove : function(){
52987         this.handleColumnMove.apply(this, arguments);
52988     },
52989     onColumnLock : function(){
52990         this.handleLockChange.apply(this, arguments);
52991     },
52992
52993     onDataChange : function(){
52994         this.refresh();
52995         this.updateHeaderSortState();
52996     },
52997
52998     onClear : function(){
52999         this.refresh();
53000     },
53001
53002     onUpdate : function(ds, record){
53003         this.refreshRow(record);
53004     },
53005
53006     refreshRow : function(record){
53007         var ds = this.ds, index;
53008         if(typeof record == 'number'){
53009             index = record;
53010             record = ds.getAt(index);
53011         }else{
53012             index = ds.indexOf(record);
53013         }
53014         this.insertRows(ds, index, index, true);
53015         this.onRemove(ds, record, index+1, true);
53016         this.syncRowHeights(index, index);
53017         this.layout();
53018         this.fireEvent("rowupdated", this, index, record);
53019     },
53020
53021     onAdd : function(ds, records, index){
53022         this.insertRows(ds, index, index + (records.length-1));
53023     },
53024
53025     onRemove : function(ds, record, index, isUpdate){
53026         if(isUpdate !== true){
53027             this.fireEvent("beforerowremoved", this, index, record);
53028         }
53029         var bt = this.getBodyTable(), lt = this.getLockedTable();
53030         if(bt.rows[index]){
53031             bt.firstChild.removeChild(bt.rows[index]);
53032         }
53033         if(lt.rows[index]){
53034             lt.firstChild.removeChild(lt.rows[index]);
53035         }
53036         if(isUpdate !== true){
53037             this.stripeRows(index);
53038             this.syncRowHeights(index, index);
53039             this.layout();
53040             this.fireEvent("rowremoved", this, index, record);
53041         }
53042     },
53043
53044     onLoad : function(){
53045         this.scrollToTop();
53046     },
53047
53048     /**
53049      * Scrolls the grid to the top
53050      */
53051     scrollToTop : function(){
53052         if(this.scroller){
53053             this.scroller.dom.scrollTop = 0;
53054             this.syncScroll();
53055         }
53056     },
53057
53058     /**
53059      * Gets a panel in the header of the grid that can be used for toolbars etc.
53060      * After modifying the contents of this panel a call to grid.autoSize() may be
53061      * required to register any changes in size.
53062      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53063      * @return Roo.Element
53064      */
53065     getHeaderPanel : function(doShow){
53066         if(doShow){
53067             this.headerPanel.show();
53068         }
53069         return this.headerPanel;
53070     },
53071
53072     /**
53073      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53074      * After modifying the contents of this panel a call to grid.autoSize() may be
53075      * required to register any changes in size.
53076      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53077      * @return Roo.Element
53078      */
53079     getFooterPanel : function(doShow){
53080         if(doShow){
53081             this.footerPanel.show();
53082         }
53083         return this.footerPanel;
53084     },
53085
53086     initElements : function(){
53087         var E = Roo.Element;
53088         var el = this.grid.getGridEl().dom.firstChild;
53089         var cs = el.childNodes;
53090
53091         this.el = new E(el);
53092         
53093          this.focusEl = new E(el.firstChild);
53094         this.focusEl.swallowEvent("click", true);
53095         
53096         this.headerPanel = new E(cs[1]);
53097         this.headerPanel.enableDisplayMode("block");
53098
53099         this.scroller = new E(cs[2]);
53100         this.scrollSizer = new E(this.scroller.dom.firstChild);
53101
53102         this.lockedWrap = new E(cs[3]);
53103         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53104         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53105
53106         this.mainWrap = new E(cs[4]);
53107         this.mainHd = new E(this.mainWrap.dom.firstChild);
53108         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53109
53110         this.footerPanel = new E(cs[5]);
53111         this.footerPanel.enableDisplayMode("block");
53112
53113         this.resizeProxy = new E(cs[6]);
53114
53115         this.headerSelector = String.format(
53116            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53117            this.lockedHd.id, this.mainHd.id
53118         );
53119
53120         this.splitterSelector = String.format(
53121            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53122            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53123         );
53124     },
53125     idToCssName : function(s)
53126     {
53127         return s.replace(/[^a-z0-9]+/ig, '-');
53128     },
53129
53130     getHeaderCell : function(index){
53131         return Roo.DomQuery.select(this.headerSelector)[index];
53132     },
53133
53134     getHeaderCellMeasure : function(index){
53135         return this.getHeaderCell(index).firstChild;
53136     },
53137
53138     getHeaderCellText : function(index){
53139         return this.getHeaderCell(index).firstChild.firstChild;
53140     },
53141
53142     getLockedTable : function(){
53143         return this.lockedBody.dom.firstChild;
53144     },
53145
53146     getBodyTable : function(){
53147         return this.mainBody.dom.firstChild;
53148     },
53149
53150     getLockedRow : function(index){
53151         return this.getLockedTable().rows[index];
53152     },
53153
53154     getRow : function(index){
53155         return this.getBodyTable().rows[index];
53156     },
53157
53158     getRowComposite : function(index){
53159         if(!this.rowEl){
53160             this.rowEl = new Roo.CompositeElementLite();
53161         }
53162         var els = [], lrow, mrow;
53163         if(lrow = this.getLockedRow(index)){
53164             els.push(lrow);
53165         }
53166         if(mrow = this.getRow(index)){
53167             els.push(mrow);
53168         }
53169         this.rowEl.elements = els;
53170         return this.rowEl;
53171     },
53172     /**
53173      * Gets the 'td' of the cell
53174      * 
53175      * @param {Integer} rowIndex row to select
53176      * @param {Integer} colIndex column to select
53177      * 
53178      * @return {Object} 
53179      */
53180     getCell : function(rowIndex, colIndex){
53181         var locked = this.cm.getLockedCount();
53182         var source;
53183         if(colIndex < locked){
53184             source = this.lockedBody.dom.firstChild;
53185         }else{
53186             source = this.mainBody.dom.firstChild;
53187             colIndex -= locked;
53188         }
53189         return source.rows[rowIndex].childNodes[colIndex];
53190     },
53191
53192     getCellText : function(rowIndex, colIndex){
53193         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53194     },
53195
53196     getCellBox : function(cell){
53197         var b = this.fly(cell).getBox();
53198         if(Roo.isOpera){ // opera fails to report the Y
53199             b.y = cell.offsetTop + this.mainBody.getY();
53200         }
53201         return b;
53202     },
53203
53204     getCellIndex : function(cell){
53205         var id = String(cell.className).match(this.cellRE);
53206         if(id){
53207             return parseInt(id[1], 10);
53208         }
53209         return 0;
53210     },
53211
53212     findHeaderIndex : function(n){
53213         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53214         return r ? this.getCellIndex(r) : false;
53215     },
53216
53217     findHeaderCell : function(n){
53218         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53219         return r ? r : false;
53220     },
53221
53222     findRowIndex : function(n){
53223         if(!n){
53224             return false;
53225         }
53226         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53227         return r ? r.rowIndex : false;
53228     },
53229
53230     findCellIndex : function(node){
53231         var stop = this.el.dom;
53232         while(node && node != stop){
53233             if(this.findRE.test(node.className)){
53234                 return this.getCellIndex(node);
53235             }
53236             node = node.parentNode;
53237         }
53238         return false;
53239     },
53240
53241     getColumnId : function(index){
53242         return this.cm.getColumnId(index);
53243     },
53244
53245     getSplitters : function()
53246     {
53247         if(this.splitterSelector){
53248            return Roo.DomQuery.select(this.splitterSelector);
53249         }else{
53250             return null;
53251       }
53252     },
53253
53254     getSplitter : function(index){
53255         return this.getSplitters()[index];
53256     },
53257
53258     onRowOver : function(e, t){
53259         var row;
53260         if((row = this.findRowIndex(t)) !== false){
53261             this.getRowComposite(row).addClass("x-grid-row-over");
53262         }
53263     },
53264
53265     onRowOut : function(e, t){
53266         var row;
53267         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53268             this.getRowComposite(row).removeClass("x-grid-row-over");
53269         }
53270     },
53271
53272     renderHeaders : function(){
53273         var cm = this.cm;
53274         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53275         var cb = [], lb = [], sb = [], lsb = [], p = {};
53276         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53277             p.cellId = "x-grid-hd-0-" + i;
53278             p.splitId = "x-grid-csplit-0-" + i;
53279             p.id = cm.getColumnId(i);
53280             p.title = cm.getColumnTooltip(i) || "";
53281             p.value = cm.getColumnHeader(i) || "";
53282             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53283             if(!cm.isLocked(i)){
53284                 cb[cb.length] = ct.apply(p);
53285                 sb[sb.length] = st.apply(p);
53286             }else{
53287                 lb[lb.length] = ct.apply(p);
53288                 lsb[lsb.length] = st.apply(p);
53289             }
53290         }
53291         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53292                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53293     },
53294
53295     updateHeaders : function(){
53296         var html = this.renderHeaders();
53297         this.lockedHd.update(html[0]);
53298         this.mainHd.update(html[1]);
53299     },
53300
53301     /**
53302      * Focuses the specified row.
53303      * @param {Number} row The row index
53304      */
53305     focusRow : function(row)
53306     {
53307         //Roo.log('GridView.focusRow');
53308         var x = this.scroller.dom.scrollLeft;
53309         this.focusCell(row, 0, false);
53310         this.scroller.dom.scrollLeft = x;
53311     },
53312
53313     /**
53314      * Focuses the specified cell.
53315      * @param {Number} row The row index
53316      * @param {Number} col The column index
53317      * @param {Boolean} hscroll false to disable horizontal scrolling
53318      */
53319     focusCell : function(row, col, hscroll)
53320     {
53321         //Roo.log('GridView.focusCell');
53322         var el = this.ensureVisible(row, col, hscroll);
53323         this.focusEl.alignTo(el, "tl-tl");
53324         if(Roo.isGecko){
53325             this.focusEl.focus();
53326         }else{
53327             this.focusEl.focus.defer(1, this.focusEl);
53328         }
53329     },
53330
53331     /**
53332      * Scrolls the specified cell into view
53333      * @param {Number} row The row index
53334      * @param {Number} col The column index
53335      * @param {Boolean} hscroll false to disable horizontal scrolling
53336      */
53337     ensureVisible : function(row, col, hscroll)
53338     {
53339         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53340         //return null; //disable for testing.
53341         if(typeof row != "number"){
53342             row = row.rowIndex;
53343         }
53344         if(row < 0 && row >= this.ds.getCount()){
53345             return  null;
53346         }
53347         col = (col !== undefined ? col : 0);
53348         var cm = this.grid.colModel;
53349         while(cm.isHidden(col)){
53350             col++;
53351         }
53352
53353         var el = this.getCell(row, col);
53354         if(!el){
53355             return null;
53356         }
53357         var c = this.scroller.dom;
53358
53359         var ctop = parseInt(el.offsetTop, 10);
53360         var cleft = parseInt(el.offsetLeft, 10);
53361         var cbot = ctop + el.offsetHeight;
53362         var cright = cleft + el.offsetWidth;
53363         
53364         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53365         var stop = parseInt(c.scrollTop, 10);
53366         var sleft = parseInt(c.scrollLeft, 10);
53367         var sbot = stop + ch;
53368         var sright = sleft + c.clientWidth;
53369         /*
53370         Roo.log('GridView.ensureVisible:' +
53371                 ' ctop:' + ctop +
53372                 ' c.clientHeight:' + c.clientHeight +
53373                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53374                 ' stop:' + stop +
53375                 ' cbot:' + cbot +
53376                 ' sbot:' + sbot +
53377                 ' ch:' + ch  
53378                 );
53379         */
53380         if(ctop < stop){
53381              c.scrollTop = ctop;
53382             //Roo.log("set scrolltop to ctop DISABLE?");
53383         }else if(cbot > sbot){
53384             //Roo.log("set scrolltop to cbot-ch");
53385             c.scrollTop = cbot-ch;
53386         }
53387         
53388         if(hscroll !== false){
53389             if(cleft < sleft){
53390                 c.scrollLeft = cleft;
53391             }else if(cright > sright){
53392                 c.scrollLeft = cright-c.clientWidth;
53393             }
53394         }
53395          
53396         return el;
53397     },
53398
53399     updateColumns : function(){
53400         this.grid.stopEditing();
53401         var cm = this.grid.colModel, colIds = this.getColumnIds();
53402         //var totalWidth = cm.getTotalWidth();
53403         var pos = 0;
53404         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53405             //if(cm.isHidden(i)) continue;
53406             var w = cm.getColumnWidth(i);
53407             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53408             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53409         }
53410         this.updateSplitters();
53411     },
53412
53413     generateRules : function(cm){
53414         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53415         Roo.util.CSS.removeStyleSheet(rulesId);
53416         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53417             var cid = cm.getColumnId(i);
53418             var align = '';
53419             if(cm.config[i].align){
53420                 align = 'text-align:'+cm.config[i].align+';';
53421             }
53422             var hidden = '';
53423             if(cm.isHidden(i)){
53424                 hidden = 'display:none;';
53425             }
53426             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53427             ruleBuf.push(
53428                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53429                     this.hdSelector, cid, " {\n", align, width, "}\n",
53430                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53431                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53432         }
53433         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53434     },
53435
53436     updateSplitters : function(){
53437         var cm = this.cm, s = this.getSplitters();
53438         if(s){ // splitters not created yet
53439             var pos = 0, locked = true;
53440             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53441                 if(cm.isHidden(i)) continue;
53442                 var w = cm.getColumnWidth(i); // make sure it's a number
53443                 if(!cm.isLocked(i) && locked){
53444                     pos = 0;
53445                     locked = false;
53446                 }
53447                 pos += w;
53448                 s[i].style.left = (pos-this.splitOffset) + "px";
53449             }
53450         }
53451     },
53452
53453     handleHiddenChange : function(colModel, colIndex, hidden){
53454         if(hidden){
53455             this.hideColumn(colIndex);
53456         }else{
53457             this.unhideColumn(colIndex);
53458         }
53459     },
53460
53461     hideColumn : function(colIndex){
53462         var cid = this.getColumnId(colIndex);
53463         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53464         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53465         if(Roo.isSafari){
53466             this.updateHeaders();
53467         }
53468         this.updateSplitters();
53469         this.layout();
53470     },
53471
53472     unhideColumn : function(colIndex){
53473         var cid = this.getColumnId(colIndex);
53474         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53475         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53476
53477         if(Roo.isSafari){
53478             this.updateHeaders();
53479         }
53480         this.updateSplitters();
53481         this.layout();
53482     },
53483
53484     insertRows : function(dm, firstRow, lastRow, isUpdate){
53485         if(firstRow == 0 && lastRow == dm.getCount()-1){
53486             this.refresh();
53487         }else{
53488             if(!isUpdate){
53489                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53490             }
53491             var s = this.getScrollState();
53492             var markup = this.renderRows(firstRow, lastRow);
53493             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53494             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53495             this.restoreScroll(s);
53496             if(!isUpdate){
53497                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53498                 this.syncRowHeights(firstRow, lastRow);
53499                 this.stripeRows(firstRow);
53500                 this.layout();
53501             }
53502         }
53503     },
53504
53505     bufferRows : function(markup, target, index){
53506         var before = null, trows = target.rows, tbody = target.tBodies[0];
53507         if(index < trows.length){
53508             before = trows[index];
53509         }
53510         var b = document.createElement("div");
53511         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53512         var rows = b.firstChild.rows;
53513         for(var i = 0, len = rows.length; i < len; i++){
53514             if(before){
53515                 tbody.insertBefore(rows[0], before);
53516             }else{
53517                 tbody.appendChild(rows[0]);
53518             }
53519         }
53520         b.innerHTML = "";
53521         b = null;
53522     },
53523
53524     deleteRows : function(dm, firstRow, lastRow){
53525         if(dm.getRowCount()<1){
53526             this.fireEvent("beforerefresh", this);
53527             this.mainBody.update("");
53528             this.lockedBody.update("");
53529             this.fireEvent("refresh", this);
53530         }else{
53531             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53532             var bt = this.getBodyTable();
53533             var tbody = bt.firstChild;
53534             var rows = bt.rows;
53535             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53536                 tbody.removeChild(rows[firstRow]);
53537             }
53538             this.stripeRows(firstRow);
53539             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53540         }
53541     },
53542
53543     updateRows : function(dataSource, firstRow, lastRow){
53544         var s = this.getScrollState();
53545         this.refresh();
53546         this.restoreScroll(s);
53547     },
53548
53549     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
53550         if(!noRefresh){
53551            this.refresh();
53552         }
53553         this.updateHeaderSortState();
53554     },
53555
53556     getScrollState : function(){
53557         
53558         var sb = this.scroller.dom;
53559         return {left: sb.scrollLeft, top: sb.scrollTop};
53560     },
53561
53562     stripeRows : function(startRow){
53563         if(!this.grid.stripeRows || this.ds.getCount() < 1){
53564             return;
53565         }
53566         startRow = startRow || 0;
53567         var rows = this.getBodyTable().rows;
53568         var lrows = this.getLockedTable().rows;
53569         var cls = ' x-grid-row-alt ';
53570         for(var i = startRow, len = rows.length; i < len; i++){
53571             var row = rows[i], lrow = lrows[i];
53572             var isAlt = ((i+1) % 2 == 0);
53573             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
53574             if(isAlt == hasAlt){
53575                 continue;
53576             }
53577             if(isAlt){
53578                 row.className += " x-grid-row-alt";
53579             }else{
53580                 row.className = row.className.replace("x-grid-row-alt", "");
53581             }
53582             if(lrow){
53583                 lrow.className = row.className;
53584             }
53585         }
53586     },
53587
53588     restoreScroll : function(state){
53589         //Roo.log('GridView.restoreScroll');
53590         var sb = this.scroller.dom;
53591         sb.scrollLeft = state.left;
53592         sb.scrollTop = state.top;
53593         this.syncScroll();
53594     },
53595
53596     syncScroll : function(){
53597         //Roo.log('GridView.syncScroll');
53598         var sb = this.scroller.dom;
53599         var sh = this.mainHd.dom;
53600         var bs = this.mainBody.dom;
53601         var lv = this.lockedBody.dom;
53602         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
53603         lv.scrollTop = bs.scrollTop = sb.scrollTop;
53604     },
53605
53606     handleScroll : function(e){
53607         this.syncScroll();
53608         var sb = this.scroller.dom;
53609         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
53610         e.stopEvent();
53611     },
53612
53613     handleWheel : function(e){
53614         var d = e.getWheelDelta();
53615         this.scroller.dom.scrollTop -= d*22;
53616         // set this here to prevent jumpy scrolling on large tables
53617         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
53618         e.stopEvent();
53619     },
53620
53621     renderRows : function(startRow, endRow){
53622         // pull in all the crap needed to render rows
53623         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
53624         var colCount = cm.getColumnCount();
53625
53626         if(ds.getCount() < 1){
53627             return ["", ""];
53628         }
53629
53630         // build a map for all the columns
53631         var cs = [];
53632         for(var i = 0; i < colCount; i++){
53633             var name = cm.getDataIndex(i);
53634             cs[i] = {
53635                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
53636                 renderer : cm.getRenderer(i),
53637                 id : cm.getColumnId(i),
53638                 locked : cm.isLocked(i)
53639             };
53640         }
53641
53642         startRow = startRow || 0;
53643         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
53644
53645         // records to render
53646         var rs = ds.getRange(startRow, endRow);
53647
53648         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
53649     },
53650
53651     // As much as I hate to duplicate code, this was branched because FireFox really hates
53652     // [].join("") on strings. The performance difference was substantial enough to
53653     // branch this function
53654     doRender : Roo.isGecko ?
53655             function(cs, rs, ds, startRow, colCount, stripe){
53656                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53657                 // buffers
53658                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53659                 
53660                 var hasListener = this.grid.hasListener('rowclass');
53661                 var rowcfg = {};
53662                 for(var j = 0, len = rs.length; j < len; j++){
53663                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
53664                     for(var i = 0; i < colCount; i++){
53665                         c = cs[i];
53666                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53667                         p.id = c.id;
53668                         p.css = p.attr = "";
53669                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53670                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53671                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53672                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53673                         }
53674                         var markup = ct.apply(p);
53675                         if(!c.locked){
53676                             cb+= markup;
53677                         }else{
53678                             lcb+= markup;
53679                         }
53680                     }
53681                     var alt = [];
53682                     if(stripe && ((rowIndex+1) % 2 == 0)){
53683                         alt.push("x-grid-row-alt")
53684                     }
53685                     if(r.dirty){
53686                         alt.push(  " x-grid-dirty-row");
53687                     }
53688                     rp.cells = lcb;
53689                     if(this.getRowClass){
53690                         alt.push(this.getRowClass(r, rowIndex));
53691                     }
53692                     if (hasListener) {
53693                         rowcfg = {
53694                              
53695                             record: r,
53696                             rowIndex : rowIndex,
53697                             rowClass : ''
53698                         }
53699                         this.grid.fireEvent('rowclass', this, rowcfg);
53700                         alt.push(rowcfg.rowClass);
53701                     }
53702                     rp.alt = alt.join(" ");
53703                     lbuf+= rt.apply(rp);
53704                     rp.cells = cb;
53705                     buf+=  rt.apply(rp);
53706                 }
53707                 return [lbuf, buf];
53708             } :
53709             function(cs, rs, ds, startRow, colCount, stripe){
53710                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53711                 // buffers
53712                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53713                 var hasListener = this.grid.hasListener('rowclass');
53714  
53715                 var rowcfg = {};
53716                 for(var j = 0, len = rs.length; j < len; j++){
53717                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
53718                     for(var i = 0; i < colCount; i++){
53719                         c = cs[i];
53720                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53721                         p.id = c.id;
53722                         p.css = p.attr = "";
53723                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53724                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53725                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53726                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53727                         }
53728                         
53729                         var markup = ct.apply(p);
53730                         if(!c.locked){
53731                             cb[cb.length] = markup;
53732                         }else{
53733                             lcb[lcb.length] = markup;
53734                         }
53735                     }
53736                     var alt = [];
53737                     if(stripe && ((rowIndex+1) % 2 == 0)){
53738                         alt.push( "x-grid-row-alt");
53739                     }
53740                     if(r.dirty){
53741                         alt.push(" x-grid-dirty-row");
53742                     }
53743                     rp.cells = lcb;
53744                     if(this.getRowClass){
53745                         alt.push( this.getRowClass(r, rowIndex));
53746                     }
53747                     if (hasListener) {
53748                         rowcfg = {
53749                              
53750                             record: r,
53751                             rowIndex : rowIndex,
53752                             rowClass : ''
53753                         }
53754                         this.grid.fireEvent('rowclass', this, rowcfg);
53755                         alt.push(rowcfg.rowClass);
53756                     }
53757                     rp.alt = alt.join(" ");
53758                     rp.cells = lcb.join("");
53759                     lbuf[lbuf.length] = rt.apply(rp);
53760                     rp.cells = cb.join("");
53761                     buf[buf.length] =  rt.apply(rp);
53762                 }
53763                 return [lbuf.join(""), buf.join("")];
53764             },
53765
53766     renderBody : function(){
53767         var markup = this.renderRows();
53768         var bt = this.templates.body;
53769         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53770     },
53771
53772     /**
53773      * Refreshes the grid
53774      * @param {Boolean} headersToo
53775      */
53776     refresh : function(headersToo){
53777         this.fireEvent("beforerefresh", this);
53778         this.grid.stopEditing();
53779         var result = this.renderBody();
53780         this.lockedBody.update(result[0]);
53781         this.mainBody.update(result[1]);
53782         if(headersToo === true){
53783             this.updateHeaders();
53784             this.updateColumns();
53785             this.updateSplitters();
53786             this.updateHeaderSortState();
53787         }
53788         this.syncRowHeights();
53789         this.layout();
53790         this.fireEvent("refresh", this);
53791     },
53792
53793     handleColumnMove : function(cm, oldIndex, newIndex){
53794         this.indexMap = null;
53795         var s = this.getScrollState();
53796         this.refresh(true);
53797         this.restoreScroll(s);
53798         this.afterMove(newIndex);
53799     },
53800
53801     afterMove : function(colIndex){
53802         if(this.enableMoveAnim && Roo.enableFx){
53803             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53804         }
53805         // if multisort - fix sortOrder, and reload..
53806         if (this.grid.dataSource.multiSort) {
53807             // the we can call sort again..
53808             var dm = this.grid.dataSource;
53809             var cm = this.grid.colModel;
53810             var so = [];
53811             for(var i = 0; i < cm.config.length; i++ ) {
53812                 
53813                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53814                     continue; // dont' bother, it's not in sort list or being set.
53815                 }
53816                 
53817                 so.push(cm.config[i].dataIndex);
53818             };
53819             dm.sortOrder = so;
53820             dm.load(dm.lastOptions);
53821             
53822             
53823         }
53824         
53825     },
53826
53827     updateCell : function(dm, rowIndex, dataIndex){
53828         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53829         if(typeof colIndex == "undefined"){ // not present in grid
53830             return;
53831         }
53832         var cm = this.grid.colModel;
53833         var cell = this.getCell(rowIndex, colIndex);
53834         var cellText = this.getCellText(rowIndex, colIndex);
53835
53836         var p = {
53837             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53838             id : cm.getColumnId(colIndex),
53839             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53840         };
53841         var renderer = cm.getRenderer(colIndex);
53842         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53843         if(typeof val == "undefined" || val === "") val = "&#160;";
53844         cellText.innerHTML = val;
53845         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53846         this.syncRowHeights(rowIndex, rowIndex);
53847     },
53848
53849     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53850         var maxWidth = 0;
53851         if(this.grid.autoSizeHeaders){
53852             var h = this.getHeaderCellMeasure(colIndex);
53853             maxWidth = Math.max(maxWidth, h.scrollWidth);
53854         }
53855         var tb, index;
53856         if(this.cm.isLocked(colIndex)){
53857             tb = this.getLockedTable();
53858             index = colIndex;
53859         }else{
53860             tb = this.getBodyTable();
53861             index = colIndex - this.cm.getLockedCount();
53862         }
53863         if(tb && tb.rows){
53864             var rows = tb.rows;
53865             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53866             for(var i = 0; i < stopIndex; i++){
53867                 var cell = rows[i].childNodes[index].firstChild;
53868                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53869             }
53870         }
53871         return maxWidth + /*margin for error in IE*/ 5;
53872     },
53873     /**
53874      * Autofit a column to its content.
53875      * @param {Number} colIndex
53876      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53877      */
53878      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53879          if(this.cm.isHidden(colIndex)){
53880              return; // can't calc a hidden column
53881          }
53882         if(forceMinSize){
53883             var cid = this.cm.getColumnId(colIndex);
53884             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53885            if(this.grid.autoSizeHeaders){
53886                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53887            }
53888         }
53889         var newWidth = this.calcColumnWidth(colIndex);
53890         this.cm.setColumnWidth(colIndex,
53891             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53892         if(!suppressEvent){
53893             this.grid.fireEvent("columnresize", colIndex, newWidth);
53894         }
53895     },
53896
53897     /**
53898      * Autofits all columns to their content and then expands to fit any extra space in the grid
53899      */
53900      autoSizeColumns : function(){
53901         var cm = this.grid.colModel;
53902         var colCount = cm.getColumnCount();
53903         for(var i = 0; i < colCount; i++){
53904             this.autoSizeColumn(i, true, true);
53905         }
53906         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53907             this.fitColumns();
53908         }else{
53909             this.updateColumns();
53910             this.layout();
53911         }
53912     },
53913
53914     /**
53915      * Autofits all columns to the grid's width proportionate with their current size
53916      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53917      */
53918     fitColumns : function(reserveScrollSpace){
53919         var cm = this.grid.colModel;
53920         var colCount = cm.getColumnCount();
53921         var cols = [];
53922         var width = 0;
53923         var i, w;
53924         for (i = 0; i < colCount; i++){
53925             if(!cm.isHidden(i) && !cm.isFixed(i)){
53926                 w = cm.getColumnWidth(i);
53927                 cols.push(i);
53928                 cols.push(w);
53929                 width += w;
53930             }
53931         }
53932         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53933         if(reserveScrollSpace){
53934             avail -= 17;
53935         }
53936         var frac = (avail - cm.getTotalWidth())/width;
53937         while (cols.length){
53938             w = cols.pop();
53939             i = cols.pop();
53940             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53941         }
53942         this.updateColumns();
53943         this.layout();
53944     },
53945
53946     onRowSelect : function(rowIndex){
53947         var row = this.getRowComposite(rowIndex);
53948         row.addClass("x-grid-row-selected");
53949     },
53950
53951     onRowDeselect : function(rowIndex){
53952         var row = this.getRowComposite(rowIndex);
53953         row.removeClass("x-grid-row-selected");
53954     },
53955
53956     onCellSelect : function(row, col){
53957         var cell = this.getCell(row, col);
53958         if(cell){
53959             Roo.fly(cell).addClass("x-grid-cell-selected");
53960         }
53961     },
53962
53963     onCellDeselect : function(row, col){
53964         var cell = this.getCell(row, col);
53965         if(cell){
53966             Roo.fly(cell).removeClass("x-grid-cell-selected");
53967         }
53968     },
53969
53970     updateHeaderSortState : function(){
53971         
53972         // sort state can be single { field: xxx, direction : yyy}
53973         // or   { xxx=>ASC , yyy : DESC ..... }
53974         
53975         var mstate = {};
53976         if (!this.ds.multiSort) { 
53977             var state = this.ds.getSortState();
53978             if(!state){
53979                 return;
53980             }
53981             mstate[state.field] = state.direction;
53982             // FIXME... - this is not used here.. but might be elsewhere..
53983             this.sortState = state;
53984             
53985         } else {
53986             mstate = this.ds.sortToggle;
53987         }
53988         //remove existing sort classes..
53989         
53990         var sc = this.sortClasses;
53991         var hds = this.el.select(this.headerSelector).removeClass(sc);
53992         
53993         for(var f in mstate) {
53994         
53995             var sortColumn = this.cm.findColumnIndex(f);
53996             
53997             if(sortColumn != -1){
53998                 var sortDir = mstate[f];        
53999                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54000             }
54001         }
54002         
54003          
54004         
54005     },
54006
54007
54008     handleHeaderClick : function(g, index,e){
54009         
54010         Roo.log("header click");
54011         
54012         if (Roo.isTouch) {
54013             // touch events on header are handled by context
54014             this.handleHdCtx(g,index,e);
54015             return;
54016         }
54017         
54018         
54019         if(this.headersDisabled){
54020             return;
54021         }
54022         var dm = g.dataSource, cm = g.colModel;
54023         if(!cm.isSortable(index)){
54024             return;
54025         }
54026         g.stopEditing();
54027         
54028         if (dm.multiSort) {
54029             // update the sortOrder
54030             var so = [];
54031             for(var i = 0; i < cm.config.length; i++ ) {
54032                 
54033                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54034                     continue; // dont' bother, it's not in sort list or being set.
54035                 }
54036                 
54037                 so.push(cm.config[i].dataIndex);
54038             };
54039             dm.sortOrder = so;
54040         }
54041         
54042         
54043         dm.sort(cm.getDataIndex(index));
54044     },
54045
54046
54047     destroy : function(){
54048         if(this.colMenu){
54049             this.colMenu.removeAll();
54050             Roo.menu.MenuMgr.unregister(this.colMenu);
54051             this.colMenu.getEl().remove();
54052             delete this.colMenu;
54053         }
54054         if(this.hmenu){
54055             this.hmenu.removeAll();
54056             Roo.menu.MenuMgr.unregister(this.hmenu);
54057             this.hmenu.getEl().remove();
54058             delete this.hmenu;
54059         }
54060         if(this.grid.enableColumnMove){
54061             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54062             if(dds){
54063                 for(var dd in dds){
54064                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54065                         var elid = dds[dd].dragElId;
54066                         dds[dd].unreg();
54067                         Roo.get(elid).remove();
54068                     } else if(dds[dd].config.isTarget){
54069                         dds[dd].proxyTop.remove();
54070                         dds[dd].proxyBottom.remove();
54071                         dds[dd].unreg();
54072                     }
54073                     if(Roo.dd.DDM.locationCache[dd]){
54074                         delete Roo.dd.DDM.locationCache[dd];
54075                     }
54076                 }
54077                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54078             }
54079         }
54080         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54081         this.bind(null, null);
54082         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54083     },
54084
54085     handleLockChange : function(){
54086         this.refresh(true);
54087     },
54088
54089     onDenyColumnLock : function(){
54090
54091     },
54092
54093     onDenyColumnHide : function(){
54094
54095     },
54096
54097     handleHdMenuClick : function(item){
54098         var index = this.hdCtxIndex;
54099         var cm = this.cm, ds = this.ds;
54100         switch(item.id){
54101             case "asc":
54102                 ds.sort(cm.getDataIndex(index), "ASC");
54103                 break;
54104             case "desc":
54105                 ds.sort(cm.getDataIndex(index), "DESC");
54106                 break;
54107             case "lock":
54108                 var lc = cm.getLockedCount();
54109                 if(cm.getColumnCount(true) <= lc+1){
54110                     this.onDenyColumnLock();
54111                     return;
54112                 }
54113                 if(lc != index){
54114                     cm.setLocked(index, true, true);
54115                     cm.moveColumn(index, lc);
54116                     this.grid.fireEvent("columnmove", index, lc);
54117                 }else{
54118                     cm.setLocked(index, true);
54119                 }
54120             break;
54121             case "unlock":
54122                 var lc = cm.getLockedCount();
54123                 if((lc-1) != index){
54124                     cm.setLocked(index, false, true);
54125                     cm.moveColumn(index, lc-1);
54126                     this.grid.fireEvent("columnmove", index, lc-1);
54127                 }else{
54128                     cm.setLocked(index, false);
54129                 }
54130             break;
54131             case 'wider': // used to expand cols on touch..
54132             case 'narrow':
54133                 var cw = cm.getColumnWidth(index);
54134                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54135                 cw = Math.max(0, cw);
54136                 cw = Math.min(cw,4000);
54137                 cm.setColumnWidth(index, cw);
54138                 break;
54139                 
54140             default:
54141                 index = cm.getIndexById(item.id.substr(4));
54142                 if(index != -1){
54143                     if(item.checked && cm.getColumnCount(true) <= 1){
54144                         this.onDenyColumnHide();
54145                         return false;
54146                     }
54147                     cm.setHidden(index, item.checked);
54148                 }
54149         }
54150         return true;
54151     },
54152
54153     beforeColMenuShow : function(){
54154         var cm = this.cm,  colCount = cm.getColumnCount();
54155         this.colMenu.removeAll();
54156         for(var i = 0; i < colCount; i++){
54157             this.colMenu.add(new Roo.menu.CheckItem({
54158                 id: "col-"+cm.getColumnId(i),
54159                 text: cm.getColumnHeader(i),
54160                 checked: !cm.isHidden(i),
54161                 hideOnClick:false
54162             }));
54163         }
54164     },
54165
54166     handleHdCtx : function(g, index, e){
54167         e.stopEvent();
54168         var hd = this.getHeaderCell(index);
54169         this.hdCtxIndex = index;
54170         var ms = this.hmenu.items, cm = this.cm;
54171         ms.get("asc").setDisabled(!cm.isSortable(index));
54172         ms.get("desc").setDisabled(!cm.isSortable(index));
54173         if(this.grid.enableColLock !== false){
54174             ms.get("lock").setDisabled(cm.isLocked(index));
54175             ms.get("unlock").setDisabled(!cm.isLocked(index));
54176         }
54177         this.hmenu.show(hd, "tl-bl");
54178     },
54179
54180     handleHdOver : function(e){
54181         var hd = this.findHeaderCell(e.getTarget());
54182         if(hd && !this.headersDisabled){
54183             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54184                this.fly(hd).addClass("x-grid-hd-over");
54185             }
54186         }
54187     },
54188
54189     handleHdOut : function(e){
54190         var hd = this.findHeaderCell(e.getTarget());
54191         if(hd){
54192             this.fly(hd).removeClass("x-grid-hd-over");
54193         }
54194     },
54195
54196     handleSplitDblClick : function(e, t){
54197         var i = this.getCellIndex(t);
54198         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54199             this.autoSizeColumn(i, true);
54200             this.layout();
54201         }
54202     },
54203
54204     render : function(){
54205
54206         var cm = this.cm;
54207         var colCount = cm.getColumnCount();
54208
54209         if(this.grid.monitorWindowResize === true){
54210             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54211         }
54212         var header = this.renderHeaders();
54213         var body = this.templates.body.apply({rows:""});
54214         var html = this.templates.master.apply({
54215             lockedBody: body,
54216             body: body,
54217             lockedHeader: header[0],
54218             header: header[1]
54219         });
54220
54221         //this.updateColumns();
54222
54223         this.grid.getGridEl().dom.innerHTML = html;
54224
54225         this.initElements();
54226         
54227         // a kludge to fix the random scolling effect in webkit
54228         this.el.on("scroll", function() {
54229             this.el.dom.scrollTop=0; // hopefully not recursive..
54230         },this);
54231
54232         this.scroller.on("scroll", this.handleScroll, this);
54233         this.lockedBody.on("mousewheel", this.handleWheel, this);
54234         this.mainBody.on("mousewheel", this.handleWheel, this);
54235
54236         this.mainHd.on("mouseover", this.handleHdOver, this);
54237         this.mainHd.on("mouseout", this.handleHdOut, this);
54238         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54239                 {delegate: "."+this.splitClass});
54240
54241         this.lockedHd.on("mouseover", this.handleHdOver, this);
54242         this.lockedHd.on("mouseout", this.handleHdOut, this);
54243         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54244                 {delegate: "."+this.splitClass});
54245
54246         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54247             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54248         }
54249
54250         this.updateSplitters();
54251
54252         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54253             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54254             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54255         }
54256
54257         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54258             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54259             this.hmenu.add(
54260                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54261                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54262             );
54263             if(this.grid.enableColLock !== false){
54264                 this.hmenu.add('-',
54265                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54266                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54267                 );
54268             }
54269             if (Roo.isTouch) {
54270                  this.hmenu.add('-',
54271                     {id:"wider", text: this.columnsWiderText},
54272                     {id:"narrow", text: this.columnsNarrowText }
54273                 );
54274                 
54275                  
54276             }
54277             
54278             if(this.grid.enableColumnHide !== false){
54279
54280                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54281                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54282                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54283
54284                 this.hmenu.add('-',
54285                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54286                 );
54287             }
54288             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54289
54290             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54291         }
54292
54293         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54294             this.dd = new Roo.grid.GridDragZone(this.grid, {
54295                 ddGroup : this.grid.ddGroup || 'GridDD'
54296             });
54297             
54298         }
54299
54300         /*
54301         for(var i = 0; i < colCount; i++){
54302             if(cm.isHidden(i)){
54303                 this.hideColumn(i);
54304             }
54305             if(cm.config[i].align){
54306                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54307                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54308             }
54309         }*/
54310         
54311         this.updateHeaderSortState();
54312
54313         this.beforeInitialResize();
54314         this.layout(true);
54315
54316         // two part rendering gives faster view to the user
54317         this.renderPhase2.defer(1, this);
54318     },
54319
54320     renderPhase2 : function(){
54321         // render the rows now
54322         this.refresh();
54323         if(this.grid.autoSizeColumns){
54324             this.autoSizeColumns();
54325         }
54326     },
54327
54328     beforeInitialResize : function(){
54329
54330     },
54331
54332     onColumnSplitterMoved : function(i, w){
54333         this.userResized = true;
54334         var cm = this.grid.colModel;
54335         cm.setColumnWidth(i, w, true);
54336         var cid = cm.getColumnId(i);
54337         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54338         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54339         this.updateSplitters();
54340         this.layout();
54341         this.grid.fireEvent("columnresize", i, w);
54342     },
54343
54344     syncRowHeights : function(startIndex, endIndex){
54345         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54346             startIndex = startIndex || 0;
54347             var mrows = this.getBodyTable().rows;
54348             var lrows = this.getLockedTable().rows;
54349             var len = mrows.length-1;
54350             endIndex = Math.min(endIndex || len, len);
54351             for(var i = startIndex; i <= endIndex; i++){
54352                 var m = mrows[i], l = lrows[i];
54353                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54354                 m.style.height = l.style.height = h + "px";
54355             }
54356         }
54357     },
54358
54359     layout : function(initialRender, is2ndPass){
54360         var g = this.grid;
54361         var auto = g.autoHeight;
54362         var scrollOffset = 16;
54363         var c = g.getGridEl(), cm = this.cm,
54364                 expandCol = g.autoExpandColumn,
54365                 gv = this;
54366         //c.beginMeasure();
54367
54368         if(!c.dom.offsetWidth){ // display:none?
54369             if(initialRender){
54370                 this.lockedWrap.show();
54371                 this.mainWrap.show();
54372             }
54373             return;
54374         }
54375
54376         var hasLock = this.cm.isLocked(0);
54377
54378         var tbh = this.headerPanel.getHeight();
54379         var bbh = this.footerPanel.getHeight();
54380
54381         if(auto){
54382             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54383             var newHeight = ch + c.getBorderWidth("tb");
54384             if(g.maxHeight){
54385                 newHeight = Math.min(g.maxHeight, newHeight);
54386             }
54387             c.setHeight(newHeight);
54388         }
54389
54390         if(g.autoWidth){
54391             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54392         }
54393
54394         var s = this.scroller;
54395
54396         var csize = c.getSize(true);
54397
54398         this.el.setSize(csize.width, csize.height);
54399
54400         this.headerPanel.setWidth(csize.width);
54401         this.footerPanel.setWidth(csize.width);
54402
54403         var hdHeight = this.mainHd.getHeight();
54404         var vw = csize.width;
54405         var vh = csize.height - (tbh + bbh);
54406
54407         s.setSize(vw, vh);
54408
54409         var bt = this.getBodyTable();
54410         var ltWidth = hasLock ?
54411                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54412
54413         var scrollHeight = bt.offsetHeight;
54414         var scrollWidth = ltWidth + bt.offsetWidth;
54415         var vscroll = false, hscroll = false;
54416
54417         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54418
54419         var lw = this.lockedWrap, mw = this.mainWrap;
54420         var lb = this.lockedBody, mb = this.mainBody;
54421
54422         setTimeout(function(){
54423             var t = s.dom.offsetTop;
54424             var w = s.dom.clientWidth,
54425                 h = s.dom.clientHeight;
54426
54427             lw.setTop(t);
54428             lw.setSize(ltWidth, h);
54429
54430             mw.setLeftTop(ltWidth, t);
54431             mw.setSize(w-ltWidth, h);
54432
54433             lb.setHeight(h-hdHeight);
54434             mb.setHeight(h-hdHeight);
54435
54436             if(is2ndPass !== true && !gv.userResized && expandCol){
54437                 // high speed resize without full column calculation
54438                 
54439                 var ci = cm.getIndexById(expandCol);
54440                 if (ci < 0) {
54441                     ci = cm.findColumnIndex(expandCol);
54442                 }
54443                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54444                 var expandId = cm.getColumnId(ci);
54445                 var  tw = cm.getTotalWidth(false);
54446                 var currentWidth = cm.getColumnWidth(ci);
54447                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54448                 if(currentWidth != cw){
54449                     cm.setColumnWidth(ci, cw, true);
54450                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54451                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54452                     gv.updateSplitters();
54453                     gv.layout(false, true);
54454                 }
54455             }
54456
54457             if(initialRender){
54458                 lw.show();
54459                 mw.show();
54460             }
54461             //c.endMeasure();
54462         }, 10);
54463     },
54464
54465     onWindowResize : function(){
54466         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54467             return;
54468         }
54469         this.layout();
54470     },
54471
54472     appendFooter : function(parentEl){
54473         return null;
54474     },
54475
54476     sortAscText : "Sort Ascending",
54477     sortDescText : "Sort Descending",
54478     lockText : "Lock Column",
54479     unlockText : "Unlock Column",
54480     columnsText : "Columns",
54481  
54482     columnsWiderText : "Wider",
54483     columnsNarrowText : "Thinner"
54484 });
54485
54486
54487 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54488     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54489     this.proxy.el.addClass('x-grid3-col-dd');
54490 };
54491
54492 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54493     handleMouseDown : function(e){
54494
54495     },
54496
54497     callHandleMouseDown : function(e){
54498         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54499     }
54500 });
54501 /*
54502  * Based on:
54503  * Ext JS Library 1.1.1
54504  * Copyright(c) 2006-2007, Ext JS, LLC.
54505  *
54506  * Originally Released Under LGPL - original licence link has changed is not relivant.
54507  *
54508  * Fork - LGPL
54509  * <script type="text/javascript">
54510  */
54511  
54512 // private
54513 // This is a support class used internally by the Grid components
54514 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54515     this.grid = grid;
54516     this.view = grid.getView();
54517     this.proxy = this.view.resizeProxy;
54518     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54519         "gridSplitters" + this.grid.getGridEl().id, {
54520         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54521     });
54522     this.setHandleElId(Roo.id(hd));
54523     this.setOuterHandleElId(Roo.id(hd2));
54524     this.scroll = false;
54525 };
54526 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54527     fly: Roo.Element.fly,
54528
54529     b4StartDrag : function(x, y){
54530         this.view.headersDisabled = true;
54531         this.proxy.setHeight(this.view.mainWrap.getHeight());
54532         var w = this.cm.getColumnWidth(this.cellIndex);
54533         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54534         this.resetConstraints();
54535         this.setXConstraint(minw, 1000);
54536         this.setYConstraint(0, 0);
54537         this.minX = x - minw;
54538         this.maxX = x + 1000;
54539         this.startPos = x;
54540         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54541     },
54542
54543
54544     handleMouseDown : function(e){
54545         ev = Roo.EventObject.setEvent(e);
54546         var t = this.fly(ev.getTarget());
54547         if(t.hasClass("x-grid-split")){
54548             this.cellIndex = this.view.getCellIndex(t.dom);
54549             this.split = t.dom;
54550             this.cm = this.grid.colModel;
54551             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
54552                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
54553             }
54554         }
54555     },
54556
54557     endDrag : function(e){
54558         this.view.headersDisabled = false;
54559         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
54560         var diff = endX - this.startPos;
54561         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
54562     },
54563
54564     autoOffset : function(){
54565         this.setDelta(0,0);
54566     }
54567 });/*
54568  * Based on:
54569  * Ext JS Library 1.1.1
54570  * Copyright(c) 2006-2007, Ext JS, LLC.
54571  *
54572  * Originally Released Under LGPL - original licence link has changed is not relivant.
54573  *
54574  * Fork - LGPL
54575  * <script type="text/javascript">
54576  */
54577  
54578 // private
54579 // This is a support class used internally by the Grid components
54580 Roo.grid.GridDragZone = function(grid, config){
54581     this.view = grid.getView();
54582     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
54583     if(this.view.lockedBody){
54584         this.setHandleElId(Roo.id(this.view.mainBody.dom));
54585         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
54586     }
54587     this.scroll = false;
54588     this.grid = grid;
54589     this.ddel = document.createElement('div');
54590     this.ddel.className = 'x-grid-dd-wrap';
54591 };
54592
54593 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
54594     ddGroup : "GridDD",
54595
54596     getDragData : function(e){
54597         var t = Roo.lib.Event.getTarget(e);
54598         var rowIndex = this.view.findRowIndex(t);
54599         var sm = this.grid.selModel;
54600             
54601         //Roo.log(rowIndex);
54602         
54603         if (sm.getSelectedCell) {
54604             // cell selection..
54605             if (!sm.getSelectedCell()) {
54606                 return false;
54607             }
54608             if (rowIndex != sm.getSelectedCell()[0]) {
54609                 return false;
54610             }
54611         
54612         }
54613         
54614         if(rowIndex !== false){
54615             
54616             // if editorgrid.. 
54617             
54618             
54619             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
54620                
54621             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
54622               //  
54623             //}
54624             if (e.hasModifier()){
54625                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
54626             }
54627             
54628             Roo.log("getDragData");
54629             
54630             return {
54631                 grid: this.grid,
54632                 ddel: this.ddel,
54633                 rowIndex: rowIndex,
54634                 selections:sm.getSelections ? sm.getSelections() : (
54635                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
54636                 )
54637             };
54638         }
54639         return false;
54640     },
54641
54642     onInitDrag : function(e){
54643         var data = this.dragData;
54644         this.ddel.innerHTML = this.grid.getDragDropText();
54645         this.proxy.update(this.ddel);
54646         // fire start drag?
54647     },
54648
54649     afterRepair : function(){
54650         this.dragging = false;
54651     },
54652
54653     getRepairXY : function(e, data){
54654         return false;
54655     },
54656
54657     onEndDrag : function(data, e){
54658         // fire end drag?
54659     },
54660
54661     onValidDrop : function(dd, e, id){
54662         // fire drag drop?
54663         this.hideProxy();
54664     },
54665
54666     beforeInvalidDrop : function(e, id){
54667
54668     }
54669 });/*
54670  * Based on:
54671  * Ext JS Library 1.1.1
54672  * Copyright(c) 2006-2007, Ext JS, LLC.
54673  *
54674  * Originally Released Under LGPL - original licence link has changed is not relivant.
54675  *
54676  * Fork - LGPL
54677  * <script type="text/javascript">
54678  */
54679  
54680
54681 /**
54682  * @class Roo.grid.ColumnModel
54683  * @extends Roo.util.Observable
54684  * This is the default implementation of a ColumnModel used by the Grid. It defines
54685  * the columns in the grid.
54686  * <br>Usage:<br>
54687  <pre><code>
54688  var colModel = new Roo.grid.ColumnModel([
54689         {header: "Ticker", width: 60, sortable: true, locked: true},
54690         {header: "Company Name", width: 150, sortable: true},
54691         {header: "Market Cap.", width: 100, sortable: true},
54692         {header: "$ Sales", width: 100, sortable: true, renderer: money},
54693         {header: "Employees", width: 100, sortable: true, resizable: false}
54694  ]);
54695  </code></pre>
54696  * <p>
54697  
54698  * The config options listed for this class are options which may appear in each
54699  * individual column definition.
54700  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
54701  * @constructor
54702  * @param {Object} config An Array of column config objects. See this class's
54703  * config objects for details.
54704 */
54705 Roo.grid.ColumnModel = function(config){
54706         /**
54707      * The config passed into the constructor
54708      */
54709     this.config = config;
54710     this.lookup = {};
54711
54712     // if no id, create one
54713     // if the column does not have a dataIndex mapping,
54714     // map it to the order it is in the config
54715     for(var i = 0, len = config.length; i < len; i++){
54716         var c = config[i];
54717         if(typeof c.dataIndex == "undefined"){
54718             c.dataIndex = i;
54719         }
54720         if(typeof c.renderer == "string"){
54721             c.renderer = Roo.util.Format[c.renderer];
54722         }
54723         if(typeof c.id == "undefined"){
54724             c.id = Roo.id();
54725         }
54726         if(c.editor && c.editor.xtype){
54727             c.editor  = Roo.factory(c.editor, Roo.grid);
54728         }
54729         if(c.editor && c.editor.isFormField){
54730             c.editor = new Roo.grid.GridEditor(c.editor);
54731         }
54732         this.lookup[c.id] = c;
54733     }
54734
54735     /**
54736      * The width of columns which have no width specified (defaults to 100)
54737      * @type Number
54738      */
54739     this.defaultWidth = 100;
54740
54741     /**
54742      * Default sortable of columns which have no sortable specified (defaults to false)
54743      * @type Boolean
54744      */
54745     this.defaultSortable = false;
54746
54747     this.addEvents({
54748         /**
54749              * @event widthchange
54750              * Fires when the width of a column changes.
54751              * @param {ColumnModel} this
54752              * @param {Number} columnIndex The column index
54753              * @param {Number} newWidth The new width
54754              */
54755             "widthchange": true,
54756         /**
54757              * @event headerchange
54758              * Fires when the text of a header changes.
54759              * @param {ColumnModel} this
54760              * @param {Number} columnIndex The column index
54761              * @param {Number} newText The new header text
54762              */
54763             "headerchange": true,
54764         /**
54765              * @event hiddenchange
54766              * Fires when a column is hidden or "unhidden".
54767              * @param {ColumnModel} this
54768              * @param {Number} columnIndex The column index
54769              * @param {Boolean} hidden true if hidden, false otherwise
54770              */
54771             "hiddenchange": true,
54772             /**
54773          * @event columnmoved
54774          * Fires when a column is moved.
54775          * @param {ColumnModel} this
54776          * @param {Number} oldIndex
54777          * @param {Number} newIndex
54778          */
54779         "columnmoved" : true,
54780         /**
54781          * @event columlockchange
54782          * Fires when a column's locked state is changed
54783          * @param {ColumnModel} this
54784          * @param {Number} colIndex
54785          * @param {Boolean} locked true if locked
54786          */
54787         "columnlockchange" : true
54788     });
54789     Roo.grid.ColumnModel.superclass.constructor.call(this);
54790 };
54791 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54792     /**
54793      * @cfg {String} header The header text to display in the Grid view.
54794      */
54795     /**
54796      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54797      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54798      * specified, the column's index is used as an index into the Record's data Array.
54799      */
54800     /**
54801      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54802      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54803      */
54804     /**
54805      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54806      * Defaults to the value of the {@link #defaultSortable} property.
54807      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54808      */
54809     /**
54810      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54811      */
54812     /**
54813      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54814      */
54815     /**
54816      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54817      */
54818     /**
54819      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54820      */
54821     /**
54822      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54823      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54824      * default renderer uses the raw data value.
54825      */
54826        /**
54827      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54828      */
54829     /**
54830      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
54831      */
54832
54833     /**
54834      * Returns the id of the column at the specified index.
54835      * @param {Number} index The column index
54836      * @return {String} the id
54837      */
54838     getColumnId : function(index){
54839         return this.config[index].id;
54840     },
54841
54842     /**
54843      * Returns the column for a specified id.
54844      * @param {String} id The column id
54845      * @return {Object} the column
54846      */
54847     getColumnById : function(id){
54848         return this.lookup[id];
54849     },
54850
54851     
54852     /**
54853      * Returns the column for a specified dataIndex.
54854      * @param {String} dataIndex The column dataIndex
54855      * @return {Object|Boolean} the column or false if not found
54856      */
54857     getColumnByDataIndex: function(dataIndex){
54858         var index = this.findColumnIndex(dataIndex);
54859         return index > -1 ? this.config[index] : false;
54860     },
54861     
54862     /**
54863      * Returns the index for a specified column id.
54864      * @param {String} id The column id
54865      * @return {Number} the index, or -1 if not found
54866      */
54867     getIndexById : function(id){
54868         for(var i = 0, len = this.config.length; i < len; i++){
54869             if(this.config[i].id == id){
54870                 return i;
54871             }
54872         }
54873         return -1;
54874     },
54875     
54876     /**
54877      * Returns the index for a specified column dataIndex.
54878      * @param {String} dataIndex The column dataIndex
54879      * @return {Number} the index, or -1 if not found
54880      */
54881     
54882     findColumnIndex : function(dataIndex){
54883         for(var i = 0, len = this.config.length; i < len; i++){
54884             if(this.config[i].dataIndex == dataIndex){
54885                 return i;
54886             }
54887         }
54888         return -1;
54889     },
54890     
54891     
54892     moveColumn : function(oldIndex, newIndex){
54893         var c = this.config[oldIndex];
54894         this.config.splice(oldIndex, 1);
54895         this.config.splice(newIndex, 0, c);
54896         this.dataMap = null;
54897         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54898     },
54899
54900     isLocked : function(colIndex){
54901         return this.config[colIndex].locked === true;
54902     },
54903
54904     setLocked : function(colIndex, value, suppressEvent){
54905         if(this.isLocked(colIndex) == value){
54906             return;
54907         }
54908         this.config[colIndex].locked = value;
54909         if(!suppressEvent){
54910             this.fireEvent("columnlockchange", this, colIndex, value);
54911         }
54912     },
54913
54914     getTotalLockedWidth : function(){
54915         var totalWidth = 0;
54916         for(var i = 0; i < this.config.length; i++){
54917             if(this.isLocked(i) && !this.isHidden(i)){
54918                 this.totalWidth += this.getColumnWidth(i);
54919             }
54920         }
54921         return totalWidth;
54922     },
54923
54924     getLockedCount : function(){
54925         for(var i = 0, len = this.config.length; i < len; i++){
54926             if(!this.isLocked(i)){
54927                 return i;
54928             }
54929         }
54930     },
54931
54932     /**
54933      * Returns the number of columns.
54934      * @return {Number}
54935      */
54936     getColumnCount : function(visibleOnly){
54937         if(visibleOnly === true){
54938             var c = 0;
54939             for(var i = 0, len = this.config.length; i < len; i++){
54940                 if(!this.isHidden(i)){
54941                     c++;
54942                 }
54943             }
54944             return c;
54945         }
54946         return this.config.length;
54947     },
54948
54949     /**
54950      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54951      * @param {Function} fn
54952      * @param {Object} scope (optional)
54953      * @return {Array} result
54954      */
54955     getColumnsBy : function(fn, scope){
54956         var r = [];
54957         for(var i = 0, len = this.config.length; i < len; i++){
54958             var c = this.config[i];
54959             if(fn.call(scope||this, c, i) === true){
54960                 r[r.length] = c;
54961             }
54962         }
54963         return r;
54964     },
54965
54966     /**
54967      * Returns true if the specified column is sortable.
54968      * @param {Number} col The column index
54969      * @return {Boolean}
54970      */
54971     isSortable : function(col){
54972         if(typeof this.config[col].sortable == "undefined"){
54973             return this.defaultSortable;
54974         }
54975         return this.config[col].sortable;
54976     },
54977
54978     /**
54979      * Returns the rendering (formatting) function defined for the column.
54980      * @param {Number} col The column index.
54981      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
54982      */
54983     getRenderer : function(col){
54984         if(!this.config[col].renderer){
54985             return Roo.grid.ColumnModel.defaultRenderer;
54986         }
54987         return this.config[col].renderer;
54988     },
54989
54990     /**
54991      * Sets the rendering (formatting) function for a column.
54992      * @param {Number} col The column index
54993      * @param {Function} fn The function to use to process the cell's raw data
54994      * to return HTML markup for the grid view. The render function is called with
54995      * the following parameters:<ul>
54996      * <li>Data value.</li>
54997      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
54998      * <li>css A CSS style string to apply to the table cell.</li>
54999      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55000      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55001      * <li>Row index</li>
55002      * <li>Column index</li>
55003      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55004      */
55005     setRenderer : function(col, fn){
55006         this.config[col].renderer = fn;
55007     },
55008
55009     /**
55010      * Returns the width for the specified column.
55011      * @param {Number} col The column index
55012      * @return {Number}
55013      */
55014     getColumnWidth : function(col){
55015         return this.config[col].width * 1 || this.defaultWidth;
55016     },
55017
55018     /**
55019      * Sets the width for a column.
55020      * @param {Number} col The column index
55021      * @param {Number} width The new width
55022      */
55023     setColumnWidth : function(col, width, suppressEvent){
55024         this.config[col].width = width;
55025         this.totalWidth = null;
55026         if(!suppressEvent){
55027              this.fireEvent("widthchange", this, col, width);
55028         }
55029     },
55030
55031     /**
55032      * Returns the total width of all columns.
55033      * @param {Boolean} includeHidden True to include hidden column widths
55034      * @return {Number}
55035      */
55036     getTotalWidth : function(includeHidden){
55037         if(!this.totalWidth){
55038             this.totalWidth = 0;
55039             for(var i = 0, len = this.config.length; i < len; i++){
55040                 if(includeHidden || !this.isHidden(i)){
55041                     this.totalWidth += this.getColumnWidth(i);
55042                 }
55043             }
55044         }
55045         return this.totalWidth;
55046     },
55047
55048     /**
55049      * Returns the header for the specified column.
55050      * @param {Number} col The column index
55051      * @return {String}
55052      */
55053     getColumnHeader : function(col){
55054         return this.config[col].header;
55055     },
55056
55057     /**
55058      * Sets the header for a column.
55059      * @param {Number} col The column index
55060      * @param {String} header The new header
55061      */
55062     setColumnHeader : function(col, header){
55063         this.config[col].header = header;
55064         this.fireEvent("headerchange", this, col, header);
55065     },
55066
55067     /**
55068      * Returns the tooltip for the specified column.
55069      * @param {Number} col The column index
55070      * @return {String}
55071      */
55072     getColumnTooltip : function(col){
55073             return this.config[col].tooltip;
55074     },
55075     /**
55076      * Sets the tooltip for a column.
55077      * @param {Number} col The column index
55078      * @param {String} tooltip The new tooltip
55079      */
55080     setColumnTooltip : function(col, tooltip){
55081             this.config[col].tooltip = tooltip;
55082     },
55083
55084     /**
55085      * Returns the dataIndex for the specified column.
55086      * @param {Number} col The column index
55087      * @return {Number}
55088      */
55089     getDataIndex : function(col){
55090         return this.config[col].dataIndex;
55091     },
55092
55093     /**
55094      * Sets the dataIndex for a column.
55095      * @param {Number} col The column index
55096      * @param {Number} dataIndex The new dataIndex
55097      */
55098     setDataIndex : function(col, dataIndex){
55099         this.config[col].dataIndex = dataIndex;
55100     },
55101
55102     
55103     
55104     /**
55105      * Returns true if the cell is editable.
55106      * @param {Number} colIndex The column index
55107      * @param {Number} rowIndex The row index
55108      * @return {Boolean}
55109      */
55110     isCellEditable : function(colIndex, rowIndex){
55111         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55112     },
55113
55114     /**
55115      * Returns the editor defined for the cell/column.
55116      * return false or null to disable editing.
55117      * @param {Number} colIndex The column index
55118      * @param {Number} rowIndex The row index
55119      * @return {Object}
55120      */
55121     getCellEditor : function(colIndex, rowIndex){
55122         return this.config[colIndex].editor;
55123     },
55124
55125     /**
55126      * Sets if a column is editable.
55127      * @param {Number} col The column index
55128      * @param {Boolean} editable True if the column is editable
55129      */
55130     setEditable : function(col, editable){
55131         this.config[col].editable = editable;
55132     },
55133
55134
55135     /**
55136      * Returns true if the column is hidden.
55137      * @param {Number} colIndex The column index
55138      * @return {Boolean}
55139      */
55140     isHidden : function(colIndex){
55141         return this.config[colIndex].hidden;
55142     },
55143
55144
55145     /**
55146      * Returns true if the column width cannot be changed
55147      */
55148     isFixed : function(colIndex){
55149         return this.config[colIndex].fixed;
55150     },
55151
55152     /**
55153      * Returns true if the column can be resized
55154      * @return {Boolean}
55155      */
55156     isResizable : function(colIndex){
55157         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55158     },
55159     /**
55160      * Sets if a column is hidden.
55161      * @param {Number} colIndex The column index
55162      * @param {Boolean} hidden True if the column is hidden
55163      */
55164     setHidden : function(colIndex, hidden){
55165         this.config[colIndex].hidden = hidden;
55166         this.totalWidth = null;
55167         this.fireEvent("hiddenchange", this, colIndex, hidden);
55168     },
55169
55170     /**
55171      * Sets the editor for a column.
55172      * @param {Number} col The column index
55173      * @param {Object} editor The editor object
55174      */
55175     setEditor : function(col, editor){
55176         this.config[col].editor = editor;
55177     }
55178 });
55179
55180 Roo.grid.ColumnModel.defaultRenderer = function(value){
55181         if(typeof value == "string" && value.length < 1){
55182             return "&#160;";
55183         }
55184         return value;
55185 };
55186
55187 // Alias for backwards compatibility
55188 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55189 /*
55190  * Based on:
55191  * Ext JS Library 1.1.1
55192  * Copyright(c) 2006-2007, Ext JS, LLC.
55193  *
55194  * Originally Released Under LGPL - original licence link has changed is not relivant.
55195  *
55196  * Fork - LGPL
55197  * <script type="text/javascript">
55198  */
55199
55200 /**
55201  * @class Roo.grid.AbstractSelectionModel
55202  * @extends Roo.util.Observable
55203  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55204  * implemented by descendant classes.  This class should not be directly instantiated.
55205  * @constructor
55206  */
55207 Roo.grid.AbstractSelectionModel = function(){
55208     this.locked = false;
55209     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55210 };
55211
55212 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55213     /** @ignore Called by the grid automatically. Do not call directly. */
55214     init : function(grid){
55215         this.grid = grid;
55216         this.initEvents();
55217     },
55218
55219     /**
55220      * Locks the selections.
55221      */
55222     lock : function(){
55223         this.locked = true;
55224     },
55225
55226     /**
55227      * Unlocks the selections.
55228      */
55229     unlock : function(){
55230         this.locked = false;
55231     },
55232
55233     /**
55234      * Returns true if the selections are locked.
55235      * @return {Boolean}
55236      */
55237     isLocked : function(){
55238         return this.locked;
55239     }
55240 });/*
55241  * Based on:
55242  * Ext JS Library 1.1.1
55243  * Copyright(c) 2006-2007, Ext JS, LLC.
55244  *
55245  * Originally Released Under LGPL - original licence link has changed is not relivant.
55246  *
55247  * Fork - LGPL
55248  * <script type="text/javascript">
55249  */
55250 /**
55251  * @extends Roo.grid.AbstractSelectionModel
55252  * @class Roo.grid.RowSelectionModel
55253  * The default SelectionModel used by {@link Roo.grid.Grid}.
55254  * It supports multiple selections and keyboard selection/navigation. 
55255  * @constructor
55256  * @param {Object} config
55257  */
55258 Roo.grid.RowSelectionModel = function(config){
55259     Roo.apply(this, config);
55260     this.selections = new Roo.util.MixedCollection(false, function(o){
55261         return o.id;
55262     });
55263
55264     this.last = false;
55265     this.lastActive = false;
55266
55267     this.addEvents({
55268         /**
55269              * @event selectionchange
55270              * Fires when the selection changes
55271              * @param {SelectionModel} this
55272              */
55273             "selectionchange" : true,
55274         /**
55275              * @event afterselectionchange
55276              * Fires after the selection changes (eg. by key press or clicking)
55277              * @param {SelectionModel} this
55278              */
55279             "afterselectionchange" : true,
55280         /**
55281              * @event beforerowselect
55282              * Fires when a row is selected being selected, return false to cancel.
55283              * @param {SelectionModel} this
55284              * @param {Number} rowIndex The selected index
55285              * @param {Boolean} keepExisting False if other selections will be cleared
55286              */
55287             "beforerowselect" : true,
55288         /**
55289              * @event rowselect
55290              * Fires when a row is selected.
55291              * @param {SelectionModel} this
55292              * @param {Number} rowIndex The selected index
55293              * @param {Roo.data.Record} r The record
55294              */
55295             "rowselect" : true,
55296         /**
55297              * @event rowdeselect
55298              * Fires when a row is deselected.
55299              * @param {SelectionModel} this
55300              * @param {Number} rowIndex The selected index
55301              */
55302         "rowdeselect" : true
55303     });
55304     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55305     this.locked = false;
55306 };
55307
55308 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55309     /**
55310      * @cfg {Boolean} singleSelect
55311      * True to allow selection of only one row at a time (defaults to false)
55312      */
55313     singleSelect : false,
55314
55315     // private
55316     initEvents : function(){
55317
55318         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55319             this.grid.on("mousedown", this.handleMouseDown, this);
55320         }else{ // allow click to work like normal
55321             this.grid.on("rowclick", this.handleDragableRowClick, this);
55322         }
55323
55324         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55325             "up" : function(e){
55326                 if(!e.shiftKey){
55327                     this.selectPrevious(e.shiftKey);
55328                 }else if(this.last !== false && this.lastActive !== false){
55329                     var last = this.last;
55330                     this.selectRange(this.last,  this.lastActive-1);
55331                     this.grid.getView().focusRow(this.lastActive);
55332                     if(last !== false){
55333                         this.last = last;
55334                     }
55335                 }else{
55336                     this.selectFirstRow();
55337                 }
55338                 this.fireEvent("afterselectionchange", this);
55339             },
55340             "down" : function(e){
55341                 if(!e.shiftKey){
55342                     this.selectNext(e.shiftKey);
55343                 }else if(this.last !== false && this.lastActive !== false){
55344                     var last = this.last;
55345                     this.selectRange(this.last,  this.lastActive+1);
55346                     this.grid.getView().focusRow(this.lastActive);
55347                     if(last !== false){
55348                         this.last = last;
55349                     }
55350                 }else{
55351                     this.selectFirstRow();
55352                 }
55353                 this.fireEvent("afterselectionchange", this);
55354             },
55355             scope: this
55356         });
55357
55358         var view = this.grid.view;
55359         view.on("refresh", this.onRefresh, this);
55360         view.on("rowupdated", this.onRowUpdated, this);
55361         view.on("rowremoved", this.onRemove, this);
55362     },
55363
55364     // private
55365     onRefresh : function(){
55366         var ds = this.grid.dataSource, i, v = this.grid.view;
55367         var s = this.selections;
55368         s.each(function(r){
55369             if((i = ds.indexOfId(r.id)) != -1){
55370                 v.onRowSelect(i);
55371             }else{
55372                 s.remove(r);
55373             }
55374         });
55375     },
55376
55377     // private
55378     onRemove : function(v, index, r){
55379         this.selections.remove(r);
55380     },
55381
55382     // private
55383     onRowUpdated : function(v, index, r){
55384         if(this.isSelected(r)){
55385             v.onRowSelect(index);
55386         }
55387     },
55388
55389     /**
55390      * Select records.
55391      * @param {Array} records The records to select
55392      * @param {Boolean} keepExisting (optional) True to keep existing selections
55393      */
55394     selectRecords : function(records, keepExisting){
55395         if(!keepExisting){
55396             this.clearSelections();
55397         }
55398         var ds = this.grid.dataSource;
55399         for(var i = 0, len = records.length; i < len; i++){
55400             this.selectRow(ds.indexOf(records[i]), true);
55401         }
55402     },
55403
55404     /**
55405      * Gets the number of selected rows.
55406      * @return {Number}
55407      */
55408     getCount : function(){
55409         return this.selections.length;
55410     },
55411
55412     /**
55413      * Selects the first row in the grid.
55414      */
55415     selectFirstRow : function(){
55416         this.selectRow(0);
55417     },
55418
55419     /**
55420      * Select the last row.
55421      * @param {Boolean} keepExisting (optional) True to keep existing selections
55422      */
55423     selectLastRow : function(keepExisting){
55424         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55425     },
55426
55427     /**
55428      * Selects the row immediately following the last selected row.
55429      * @param {Boolean} keepExisting (optional) True to keep existing selections
55430      */
55431     selectNext : function(keepExisting){
55432         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55433             this.selectRow(this.last+1, keepExisting);
55434             this.grid.getView().focusRow(this.last);
55435         }
55436     },
55437
55438     /**
55439      * Selects the row that precedes the last selected row.
55440      * @param {Boolean} keepExisting (optional) True to keep existing selections
55441      */
55442     selectPrevious : function(keepExisting){
55443         if(this.last){
55444             this.selectRow(this.last-1, keepExisting);
55445             this.grid.getView().focusRow(this.last);
55446         }
55447     },
55448
55449     /**
55450      * Returns the selected records
55451      * @return {Array} Array of selected records
55452      */
55453     getSelections : function(){
55454         return [].concat(this.selections.items);
55455     },
55456
55457     /**
55458      * Returns the first selected record.
55459      * @return {Record}
55460      */
55461     getSelected : function(){
55462         return this.selections.itemAt(0);
55463     },
55464
55465
55466     /**
55467      * Clears all selections.
55468      */
55469     clearSelections : function(fast){
55470         if(this.locked) return;
55471         if(fast !== true){
55472             var ds = this.grid.dataSource;
55473             var s = this.selections;
55474             s.each(function(r){
55475                 this.deselectRow(ds.indexOfId(r.id));
55476             }, this);
55477             s.clear();
55478         }else{
55479             this.selections.clear();
55480         }
55481         this.last = false;
55482     },
55483
55484
55485     /**
55486      * Selects all rows.
55487      */
55488     selectAll : function(){
55489         if(this.locked) return;
55490         this.selections.clear();
55491         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55492             this.selectRow(i, true);
55493         }
55494     },
55495
55496     /**
55497      * Returns True if there is a selection.
55498      * @return {Boolean}
55499      */
55500     hasSelection : function(){
55501         return this.selections.length > 0;
55502     },
55503
55504     /**
55505      * Returns True if the specified row is selected.
55506      * @param {Number/Record} record The record or index of the record to check
55507      * @return {Boolean}
55508      */
55509     isSelected : function(index){
55510         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55511         return (r && this.selections.key(r.id) ? true : false);
55512     },
55513
55514     /**
55515      * Returns True if the specified record id is selected.
55516      * @param {String} id The id of record to check
55517      * @return {Boolean}
55518      */
55519     isIdSelected : function(id){
55520         return (this.selections.key(id) ? true : false);
55521     },
55522
55523     // private
55524     handleMouseDown : function(e, t){
55525         var view = this.grid.getView(), rowIndex;
55526         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55527             return;
55528         };
55529         if(e.shiftKey && this.last !== false){
55530             var last = this.last;
55531             this.selectRange(last, rowIndex, e.ctrlKey);
55532             this.last = last; // reset the last
55533             view.focusRow(rowIndex);
55534         }else{
55535             var isSelected = this.isSelected(rowIndex);
55536             if(e.button !== 0 && isSelected){
55537                 view.focusRow(rowIndex);
55538             }else if(e.ctrlKey && isSelected){
55539                 this.deselectRow(rowIndex);
55540             }else if(!isSelected){
55541                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
55542                 view.focusRow(rowIndex);
55543             }
55544         }
55545         this.fireEvent("afterselectionchange", this);
55546     },
55547     // private
55548     handleDragableRowClick :  function(grid, rowIndex, e) 
55549     {
55550         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
55551             this.selectRow(rowIndex, false);
55552             grid.view.focusRow(rowIndex);
55553              this.fireEvent("afterselectionchange", this);
55554         }
55555     },
55556     
55557     /**
55558      * Selects multiple rows.
55559      * @param {Array} rows Array of the indexes of the row to select
55560      * @param {Boolean} keepExisting (optional) True to keep existing selections
55561      */
55562     selectRows : function(rows, keepExisting){
55563         if(!keepExisting){
55564             this.clearSelections();
55565         }
55566         for(var i = 0, len = rows.length; i < len; i++){
55567             this.selectRow(rows[i], true);
55568         }
55569     },
55570
55571     /**
55572      * Selects a range of rows. All rows in between startRow and endRow are also selected.
55573      * @param {Number} startRow The index of the first row in the range
55574      * @param {Number} endRow The index of the last row in the range
55575      * @param {Boolean} keepExisting (optional) True to retain existing selections
55576      */
55577     selectRange : function(startRow, endRow, keepExisting){
55578         if(this.locked) return;
55579         if(!keepExisting){
55580             this.clearSelections();
55581         }
55582         if(startRow <= endRow){
55583             for(var i = startRow; i <= endRow; i++){
55584                 this.selectRow(i, true);
55585             }
55586         }else{
55587             for(var i = startRow; i >= endRow; i--){
55588                 this.selectRow(i, true);
55589             }
55590         }
55591     },
55592
55593     /**
55594      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
55595      * @param {Number} startRow The index of the first row in the range
55596      * @param {Number} endRow The index of the last row in the range
55597      */
55598     deselectRange : function(startRow, endRow, preventViewNotify){
55599         if(this.locked) return;
55600         for(var i = startRow; i <= endRow; i++){
55601             this.deselectRow(i, preventViewNotify);
55602         }
55603     },
55604
55605     /**
55606      * Selects a row.
55607      * @param {Number} row The index of the row to select
55608      * @param {Boolean} keepExisting (optional) True to keep existing selections
55609      */
55610     selectRow : function(index, keepExisting, preventViewNotify){
55611         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
55612         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
55613             if(!keepExisting || this.singleSelect){
55614                 this.clearSelections();
55615             }
55616             var r = this.grid.dataSource.getAt(index);
55617             this.selections.add(r);
55618             this.last = this.lastActive = index;
55619             if(!preventViewNotify){
55620                 this.grid.getView().onRowSelect(index);
55621             }
55622             this.fireEvent("rowselect", this, index, r);
55623             this.fireEvent("selectionchange", this);
55624         }
55625     },
55626
55627     /**
55628      * Deselects a row.
55629      * @param {Number} row The index of the row to deselect
55630      */
55631     deselectRow : function(index, preventViewNotify){
55632         if(this.locked) return;
55633         if(this.last == index){
55634             this.last = false;
55635         }
55636         if(this.lastActive == index){
55637             this.lastActive = false;
55638         }
55639         var r = this.grid.dataSource.getAt(index);
55640         this.selections.remove(r);
55641         if(!preventViewNotify){
55642             this.grid.getView().onRowDeselect(index);
55643         }
55644         this.fireEvent("rowdeselect", this, index);
55645         this.fireEvent("selectionchange", this);
55646     },
55647
55648     // private
55649     restoreLast : function(){
55650         if(this._last){
55651             this.last = this._last;
55652         }
55653     },
55654
55655     // private
55656     acceptsNav : function(row, col, cm){
55657         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55658     },
55659
55660     // private
55661     onEditorKey : function(field, e){
55662         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
55663         if(k == e.TAB){
55664             e.stopEvent();
55665             ed.completeEdit();
55666             if(e.shiftKey){
55667                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55668             }else{
55669                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55670             }
55671         }else if(k == e.ENTER && !e.ctrlKey){
55672             e.stopEvent();
55673             ed.completeEdit();
55674             if(e.shiftKey){
55675                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
55676             }else{
55677                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
55678             }
55679         }else if(k == e.ESC){
55680             ed.cancelEdit();
55681         }
55682         if(newCell){
55683             g.startEditing(newCell[0], newCell[1]);
55684         }
55685     }
55686 });/*
55687  * Based on:
55688  * Ext JS Library 1.1.1
55689  * Copyright(c) 2006-2007, Ext JS, LLC.
55690  *
55691  * Originally Released Under LGPL - original licence link has changed is not relivant.
55692  *
55693  * Fork - LGPL
55694  * <script type="text/javascript">
55695  */
55696 /**
55697  * @class Roo.grid.CellSelectionModel
55698  * @extends Roo.grid.AbstractSelectionModel
55699  * This class provides the basic implementation for cell selection in a grid.
55700  * @constructor
55701  * @param {Object} config The object containing the configuration of this model.
55702  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
55703  */
55704 Roo.grid.CellSelectionModel = function(config){
55705     Roo.apply(this, config);
55706
55707     this.selection = null;
55708
55709     this.addEvents({
55710         /**
55711              * @event beforerowselect
55712              * Fires before a cell is selected.
55713              * @param {SelectionModel} this
55714              * @param {Number} rowIndex The selected row index
55715              * @param {Number} colIndex The selected cell index
55716              */
55717             "beforecellselect" : true,
55718         /**
55719              * @event cellselect
55720              * Fires when a cell is selected.
55721              * @param {SelectionModel} this
55722              * @param {Number} rowIndex The selected row index
55723              * @param {Number} colIndex The selected cell index
55724              */
55725             "cellselect" : true,
55726         /**
55727              * @event selectionchange
55728              * Fires when the active selection changes.
55729              * @param {SelectionModel} this
55730              * @param {Object} selection null for no selection or an object (o) with two properties
55731                 <ul>
55732                 <li>o.record: the record object for the row the selection is in</li>
55733                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
55734                 </ul>
55735              */
55736             "selectionchange" : true,
55737         /**
55738              * @event tabend
55739              * Fires when the tab (or enter) was pressed on the last editable cell
55740              * You can use this to trigger add new row.
55741              * @param {SelectionModel} this
55742              */
55743             "tabend" : true,
55744          /**
55745              * @event beforeeditnext
55746              * Fires before the next editable sell is made active
55747              * You can use this to skip to another cell or fire the tabend
55748              *    if you set cell to false
55749              * @param {Object} eventdata object : { cell : [ row, col ] } 
55750              */
55751             "beforeeditnext" : true
55752     });
55753     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
55754 };
55755
55756 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
55757     
55758     enter_is_tab: false,
55759
55760     /** @ignore */
55761     initEvents : function(){
55762         this.grid.on("mousedown", this.handleMouseDown, this);
55763         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
55764         var view = this.grid.view;
55765         view.on("refresh", this.onViewChange, this);
55766         view.on("rowupdated", this.onRowUpdated, this);
55767         view.on("beforerowremoved", this.clearSelections, this);
55768         view.on("beforerowsinserted", this.clearSelections, this);
55769         if(this.grid.isEditor){
55770             this.grid.on("beforeedit", this.beforeEdit,  this);
55771         }
55772     },
55773
55774         //private
55775     beforeEdit : function(e){
55776         this.select(e.row, e.column, false, true, e.record);
55777     },
55778
55779         //private
55780     onRowUpdated : function(v, index, r){
55781         if(this.selection && this.selection.record == r){
55782             v.onCellSelect(index, this.selection.cell[1]);
55783         }
55784     },
55785
55786         //private
55787     onViewChange : function(){
55788         this.clearSelections(true);
55789     },
55790
55791         /**
55792          * Returns the currently selected cell,.
55793          * @return {Array} The selected cell (row, column) or null if none selected.
55794          */
55795     getSelectedCell : function(){
55796         return this.selection ? this.selection.cell : null;
55797     },
55798
55799     /**
55800      * Clears all selections.
55801      * @param {Boolean} true to prevent the gridview from being notified about the change.
55802      */
55803     clearSelections : function(preventNotify){
55804         var s = this.selection;
55805         if(s){
55806             if(preventNotify !== true){
55807                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55808             }
55809             this.selection = null;
55810             this.fireEvent("selectionchange", this, null);
55811         }
55812     },
55813
55814     /**
55815      * Returns true if there is a selection.
55816      * @return {Boolean}
55817      */
55818     hasSelection : function(){
55819         return this.selection ? true : false;
55820     },
55821
55822     /** @ignore */
55823     handleMouseDown : function(e, t){
55824         var v = this.grid.getView();
55825         if(this.isLocked()){
55826             return;
55827         };
55828         var row = v.findRowIndex(t);
55829         var cell = v.findCellIndex(t);
55830         if(row !== false && cell !== false){
55831             this.select(row, cell);
55832         }
55833     },
55834
55835     /**
55836      * Selects a cell.
55837      * @param {Number} rowIndex
55838      * @param {Number} collIndex
55839      */
55840     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
55841         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
55842             this.clearSelections();
55843             r = r || this.grid.dataSource.getAt(rowIndex);
55844             this.selection = {
55845                 record : r,
55846                 cell : [rowIndex, colIndex]
55847             };
55848             if(!preventViewNotify){
55849                 var v = this.grid.getView();
55850                 v.onCellSelect(rowIndex, colIndex);
55851                 if(preventFocus !== true){
55852                     v.focusCell(rowIndex, colIndex);
55853                 }
55854             }
55855             this.fireEvent("cellselect", this, rowIndex, colIndex);
55856             this.fireEvent("selectionchange", this, this.selection);
55857         }
55858     },
55859
55860         //private
55861     isSelectable : function(rowIndex, colIndex, cm){
55862         return !cm.isHidden(colIndex);
55863     },
55864
55865     /** @ignore */
55866     handleKeyDown : function(e){
55867         //Roo.log('Cell Sel Model handleKeyDown');
55868         if(!e.isNavKeyPress()){
55869             return;
55870         }
55871         var g = this.grid, s = this.selection;
55872         if(!s){
55873             e.stopEvent();
55874             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55875             if(cell){
55876                 this.select(cell[0], cell[1]);
55877             }
55878             return;
55879         }
55880         var sm = this;
55881         var walk = function(row, col, step){
55882             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55883         };
55884         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55885         var newCell;
55886
55887       
55888
55889         switch(k){
55890             case e.TAB:
55891                 // handled by onEditorKey
55892                 if (g.isEditor && g.editing) {
55893                     return;
55894                 }
55895                 if(e.shiftKey) {
55896                     newCell = walk(r, c-1, -1);
55897                 } else {
55898                     newCell = walk(r, c+1, 1);
55899                 }
55900                 break;
55901             
55902             case e.DOWN:
55903                newCell = walk(r+1, c, 1);
55904                 break;
55905             
55906             case e.UP:
55907                 newCell = walk(r-1, c, -1);
55908                 break;
55909             
55910             case e.RIGHT:
55911                 newCell = walk(r, c+1, 1);
55912                 break;
55913             
55914             case e.LEFT:
55915                 newCell = walk(r, c-1, -1);
55916                 break;
55917             
55918             case e.ENTER:
55919                 
55920                 if(g.isEditor && !g.editing){
55921                    g.startEditing(r, c);
55922                    e.stopEvent();
55923                    return;
55924                 }
55925                 
55926                 
55927              break;
55928         };
55929         if(newCell){
55930             this.select(newCell[0], newCell[1]);
55931             e.stopEvent();
55932             
55933         }
55934     },
55935
55936     acceptsNav : function(row, col, cm){
55937         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55938     },
55939     /**
55940      * Selects a cell.
55941      * @param {Number} field (not used) - as it's normally used as a listener
55942      * @param {Number} e - event - fake it by using
55943      *
55944      * var e = Roo.EventObjectImpl.prototype;
55945      * e.keyCode = e.TAB
55946      *
55947      * 
55948      */
55949     onEditorKey : function(field, e){
55950         
55951         var k = e.getKey(),
55952             newCell,
55953             g = this.grid,
55954             ed = g.activeEditor,
55955             forward = false;
55956         ///Roo.log('onEditorKey' + k);
55957         
55958         
55959         if (this.enter_is_tab && k == e.ENTER) {
55960             k = e.TAB;
55961         }
55962         
55963         if(k == e.TAB){
55964             if(e.shiftKey){
55965                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55966             }else{
55967                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55968                 forward = true;
55969             }
55970             
55971             e.stopEvent();
55972             
55973         } else if(k == e.ENTER &&  !e.ctrlKey){
55974             ed.completeEdit();
55975             e.stopEvent();
55976             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55977         
55978                 } else if(k == e.ESC){
55979             ed.cancelEdit();
55980         }
55981                 
55982         if (newCell) {
55983             var ecall = { cell : newCell, forward : forward };
55984             this.fireEvent('beforeeditnext', ecall );
55985             newCell = ecall.cell;
55986                         forward = ecall.forward;
55987         }
55988                 
55989         if(newCell){
55990             //Roo.log('next cell after edit');
55991             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
55992         } else if (forward) {
55993             // tabbed past last
55994             this.fireEvent.defer(100, this, ['tabend',this]);
55995         }
55996     }
55997 });/*
55998  * Based on:
55999  * Ext JS Library 1.1.1
56000  * Copyright(c) 2006-2007, Ext JS, LLC.
56001  *
56002  * Originally Released Under LGPL - original licence link has changed is not relivant.
56003  *
56004  * Fork - LGPL
56005  * <script type="text/javascript">
56006  */
56007  
56008 /**
56009  * @class Roo.grid.EditorGrid
56010  * @extends Roo.grid.Grid
56011  * Class for creating and editable grid.
56012  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56013  * The container MUST have some type of size defined for the grid to fill. The container will be 
56014  * automatically set to position relative if it isn't already.
56015  * @param {Object} dataSource The data model to bind to
56016  * @param {Object} colModel The column model with info about this grid's columns
56017  */
56018 Roo.grid.EditorGrid = function(container, config){
56019     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56020     this.getGridEl().addClass("xedit-grid");
56021
56022     if(!this.selModel){
56023         this.selModel = new Roo.grid.CellSelectionModel();
56024     }
56025
56026     this.activeEditor = null;
56027
56028         this.addEvents({
56029             /**
56030              * @event beforeedit
56031              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56032              * <ul style="padding:5px;padding-left:16px;">
56033              * <li>grid - This grid</li>
56034              * <li>record - The record being edited</li>
56035              * <li>field - The field name being edited</li>
56036              * <li>value - The value for the field being edited.</li>
56037              * <li>row - The grid row index</li>
56038              * <li>column - The grid column index</li>
56039              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56040              * </ul>
56041              * @param {Object} e An edit event (see above for description)
56042              */
56043             "beforeedit" : true,
56044             /**
56045              * @event afteredit
56046              * Fires after a cell is edited. <br />
56047              * <ul style="padding:5px;padding-left:16px;">
56048              * <li>grid - This grid</li>
56049              * <li>record - The record being edited</li>
56050              * <li>field - The field name being edited</li>
56051              * <li>value - The value being set</li>
56052              * <li>originalValue - The original value for the field, before the edit.</li>
56053              * <li>row - The grid row index</li>
56054              * <li>column - The grid column index</li>
56055              * </ul>
56056              * @param {Object} e An edit event (see above for description)
56057              */
56058             "afteredit" : true,
56059             /**
56060              * @event validateedit
56061              * Fires after a cell is edited, but before the value is set in the record. 
56062          * You can use this to modify the value being set in the field, Return false
56063              * to cancel the change. The edit event object has the following properties <br />
56064              * <ul style="padding:5px;padding-left:16px;">
56065          * <li>editor - This editor</li>
56066              * <li>grid - This grid</li>
56067              * <li>record - The record being edited</li>
56068              * <li>field - The field name being edited</li>
56069              * <li>value - The value being set</li>
56070              * <li>originalValue - The original value for the field, before the edit.</li>
56071              * <li>row - The grid row index</li>
56072              * <li>column - The grid column index</li>
56073              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56074              * </ul>
56075              * @param {Object} e An edit event (see above for description)
56076              */
56077             "validateedit" : true
56078         });
56079     this.on("bodyscroll", this.stopEditing,  this);
56080     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56081 };
56082
56083 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56084     /**
56085      * @cfg {Number} clicksToEdit
56086      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56087      */
56088     clicksToEdit: 2,
56089
56090     // private
56091     isEditor : true,
56092     // private
56093     trackMouseOver: false, // causes very odd FF errors
56094
56095     onCellDblClick : function(g, row, col){
56096         this.startEditing(row, col);
56097     },
56098
56099     onEditComplete : function(ed, value, startValue){
56100         this.editing = false;
56101         this.activeEditor = null;
56102         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56103         var r = ed.record;
56104         var field = this.colModel.getDataIndex(ed.col);
56105         var e = {
56106             grid: this,
56107             record: r,
56108             field: field,
56109             originalValue: startValue,
56110             value: value,
56111             row: ed.row,
56112             column: ed.col,
56113             cancel:false,
56114             editor: ed
56115         };
56116         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56117         cell.show();
56118           
56119         if(String(value) !== String(startValue)){
56120             
56121             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56122                 r.set(field, e.value);
56123                 // if we are dealing with a combo box..
56124                 // then we also set the 'name' colum to be the displayField
56125                 if (ed.field.displayField && ed.field.name) {
56126                     r.set(ed.field.name, ed.field.el.dom.value);
56127                 }
56128                 
56129                 delete e.cancel; //?? why!!!
56130                 this.fireEvent("afteredit", e);
56131             }
56132         } else {
56133             this.fireEvent("afteredit", e); // always fire it!
56134         }
56135         this.view.focusCell(ed.row, ed.col);
56136     },
56137
56138     /**
56139      * Starts editing the specified for the specified row/column
56140      * @param {Number} rowIndex
56141      * @param {Number} colIndex
56142      */
56143     startEditing : function(row, col){
56144         this.stopEditing();
56145         if(this.colModel.isCellEditable(col, row)){
56146             this.view.ensureVisible(row, col, true);
56147           
56148             var r = this.dataSource.getAt(row);
56149             var field = this.colModel.getDataIndex(col);
56150             var cell = Roo.get(this.view.getCell(row,col));
56151             var e = {
56152                 grid: this,
56153                 record: r,
56154                 field: field,
56155                 value: r.data[field],
56156                 row: row,
56157                 column: col,
56158                 cancel:false 
56159             };
56160             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56161                 this.editing = true;
56162                 var ed = this.colModel.getCellEditor(col, row);
56163                 
56164                 if (!ed) {
56165                     return;
56166                 }
56167                 if(!ed.rendered){
56168                     ed.render(ed.parentEl || document.body);
56169                 }
56170                 ed.field.reset();
56171                
56172                 cell.hide();
56173                 
56174                 (function(){ // complex but required for focus issues in safari, ie and opera
56175                     ed.row = row;
56176                     ed.col = col;
56177                     ed.record = r;
56178                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56179                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56180                     this.activeEditor = ed;
56181                     var v = r.data[field];
56182                     ed.startEdit(this.view.getCell(row, col), v);
56183                     // combo's with 'displayField and name set
56184                     if (ed.field.displayField && ed.field.name) {
56185                         ed.field.el.dom.value = r.data[ed.field.name];
56186                     }
56187                     
56188                     
56189                 }).defer(50, this);
56190             }
56191         }
56192     },
56193         
56194     /**
56195      * Stops any active editing
56196      */
56197     stopEditing : function(){
56198         if(this.activeEditor){
56199             this.activeEditor.completeEdit();
56200         }
56201         this.activeEditor = null;
56202     },
56203         
56204          /**
56205      * Called to get grid's drag proxy text, by default returns this.ddText.
56206      * @return {String}
56207      */
56208     getDragDropText : function(){
56209         var count = this.selModel.getSelectedCell() ? 1 : 0;
56210         return String.format(this.ddText, count, count == 1 ? '' : 's');
56211     }
56212         
56213 });/*
56214  * Based on:
56215  * Ext JS Library 1.1.1
56216  * Copyright(c) 2006-2007, Ext JS, LLC.
56217  *
56218  * Originally Released Under LGPL - original licence link has changed is not relivant.
56219  *
56220  * Fork - LGPL
56221  * <script type="text/javascript">
56222  */
56223
56224 // private - not really -- you end up using it !
56225 // This is a support class used internally by the Grid components
56226
56227 /**
56228  * @class Roo.grid.GridEditor
56229  * @extends Roo.Editor
56230  * Class for creating and editable grid elements.
56231  * @param {Object} config any settings (must include field)
56232  */
56233 Roo.grid.GridEditor = function(field, config){
56234     if (!config && field.field) {
56235         config = field;
56236         field = Roo.factory(config.field, Roo.form);
56237     }
56238     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56239     field.monitorTab = false;
56240 };
56241
56242 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56243     
56244     /**
56245      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56246      */
56247     
56248     alignment: "tl-tl",
56249     autoSize: "width",
56250     hideEl : false,
56251     cls: "x-small-editor x-grid-editor",
56252     shim:false,
56253     shadow:"frame"
56254 });/*
56255  * Based on:
56256  * Ext JS Library 1.1.1
56257  * Copyright(c) 2006-2007, Ext JS, LLC.
56258  *
56259  * Originally Released Under LGPL - original licence link has changed is not relivant.
56260  *
56261  * Fork - LGPL
56262  * <script type="text/javascript">
56263  */
56264   
56265
56266   
56267 Roo.grid.PropertyRecord = Roo.data.Record.create([
56268     {name:'name',type:'string'},  'value'
56269 ]);
56270
56271
56272 Roo.grid.PropertyStore = function(grid, source){
56273     this.grid = grid;
56274     this.store = new Roo.data.Store({
56275         recordType : Roo.grid.PropertyRecord
56276     });
56277     this.store.on('update', this.onUpdate,  this);
56278     if(source){
56279         this.setSource(source);
56280     }
56281     Roo.grid.PropertyStore.superclass.constructor.call(this);
56282 };
56283
56284
56285
56286 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56287     setSource : function(o){
56288         this.source = o;
56289         this.store.removeAll();
56290         var data = [];
56291         for(var k in o){
56292             if(this.isEditableValue(o[k])){
56293                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56294             }
56295         }
56296         this.store.loadRecords({records: data}, {}, true);
56297     },
56298
56299     onUpdate : function(ds, record, type){
56300         if(type == Roo.data.Record.EDIT){
56301             var v = record.data['value'];
56302             var oldValue = record.modified['value'];
56303             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56304                 this.source[record.id] = v;
56305                 record.commit();
56306                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56307             }else{
56308                 record.reject();
56309             }
56310         }
56311     },
56312
56313     getProperty : function(row){
56314        return this.store.getAt(row);
56315     },
56316
56317     isEditableValue: function(val){
56318         if(val && val instanceof Date){
56319             return true;
56320         }else if(typeof val == 'object' || typeof val == 'function'){
56321             return false;
56322         }
56323         return true;
56324     },
56325
56326     setValue : function(prop, value){
56327         this.source[prop] = value;
56328         this.store.getById(prop).set('value', value);
56329     },
56330
56331     getSource : function(){
56332         return this.source;
56333     }
56334 });
56335
56336 Roo.grid.PropertyColumnModel = function(grid, store){
56337     this.grid = grid;
56338     var g = Roo.grid;
56339     g.PropertyColumnModel.superclass.constructor.call(this, [
56340         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56341         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56342     ]);
56343     this.store = store;
56344     this.bselect = Roo.DomHelper.append(document.body, {
56345         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56346             {tag: 'option', value: 'true', html: 'true'},
56347             {tag: 'option', value: 'false', html: 'false'}
56348         ]
56349     });
56350     Roo.id(this.bselect);
56351     var f = Roo.form;
56352     this.editors = {
56353         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56354         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56355         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56356         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56357         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56358     };
56359     this.renderCellDelegate = this.renderCell.createDelegate(this);
56360     this.renderPropDelegate = this.renderProp.createDelegate(this);
56361 };
56362
56363 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56364     
56365     
56366     nameText : 'Name',
56367     valueText : 'Value',
56368     
56369     dateFormat : 'm/j/Y',
56370     
56371     
56372     renderDate : function(dateVal){
56373         return dateVal.dateFormat(this.dateFormat);
56374     },
56375
56376     renderBool : function(bVal){
56377         return bVal ? 'true' : 'false';
56378     },
56379
56380     isCellEditable : function(colIndex, rowIndex){
56381         return colIndex == 1;
56382     },
56383
56384     getRenderer : function(col){
56385         return col == 1 ?
56386             this.renderCellDelegate : this.renderPropDelegate;
56387     },
56388
56389     renderProp : function(v){
56390         return this.getPropertyName(v);
56391     },
56392
56393     renderCell : function(val){
56394         var rv = val;
56395         if(val instanceof Date){
56396             rv = this.renderDate(val);
56397         }else if(typeof val == 'boolean'){
56398             rv = this.renderBool(val);
56399         }
56400         return Roo.util.Format.htmlEncode(rv);
56401     },
56402
56403     getPropertyName : function(name){
56404         var pn = this.grid.propertyNames;
56405         return pn && pn[name] ? pn[name] : name;
56406     },
56407
56408     getCellEditor : function(colIndex, rowIndex){
56409         var p = this.store.getProperty(rowIndex);
56410         var n = p.data['name'], val = p.data['value'];
56411         
56412         if(typeof(this.grid.customEditors[n]) == 'string'){
56413             return this.editors[this.grid.customEditors[n]];
56414         }
56415         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56416             return this.grid.customEditors[n];
56417         }
56418         if(val instanceof Date){
56419             return this.editors['date'];
56420         }else if(typeof val == 'number'){
56421             return this.editors['number'];
56422         }else if(typeof val == 'boolean'){
56423             return this.editors['boolean'];
56424         }else{
56425             return this.editors['string'];
56426         }
56427     }
56428 });
56429
56430 /**
56431  * @class Roo.grid.PropertyGrid
56432  * @extends Roo.grid.EditorGrid
56433  * This class represents the  interface of a component based property grid control.
56434  * <br><br>Usage:<pre><code>
56435  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56436       
56437  });
56438  // set any options
56439  grid.render();
56440  * </code></pre>
56441   
56442  * @constructor
56443  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56444  * The container MUST have some type of size defined for the grid to fill. The container will be
56445  * automatically set to position relative if it isn't already.
56446  * @param {Object} config A config object that sets properties on this grid.
56447  */
56448 Roo.grid.PropertyGrid = function(container, config){
56449     config = config || {};
56450     var store = new Roo.grid.PropertyStore(this);
56451     this.store = store;
56452     var cm = new Roo.grid.PropertyColumnModel(this, store);
56453     store.store.sort('name', 'ASC');
56454     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56455         ds: store.store,
56456         cm: cm,
56457         enableColLock:false,
56458         enableColumnMove:false,
56459         stripeRows:false,
56460         trackMouseOver: false,
56461         clicksToEdit:1
56462     }, config));
56463     this.getGridEl().addClass('x-props-grid');
56464     this.lastEditRow = null;
56465     this.on('columnresize', this.onColumnResize, this);
56466     this.addEvents({
56467          /**
56468              * @event beforepropertychange
56469              * Fires before a property changes (return false to stop?)
56470              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56471              * @param {String} id Record Id
56472              * @param {String} newval New Value
56473          * @param {String} oldval Old Value
56474              */
56475         "beforepropertychange": true,
56476         /**
56477              * @event propertychange
56478              * Fires after a property changes
56479              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56480              * @param {String} id Record Id
56481              * @param {String} newval New Value
56482          * @param {String} oldval Old Value
56483              */
56484         "propertychange": true
56485     });
56486     this.customEditors = this.customEditors || {};
56487 };
56488 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56489     
56490      /**
56491      * @cfg {Object} customEditors map of colnames=> custom editors.
56492      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56493      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56494      * false disables editing of the field.
56495          */
56496     
56497       /**
56498      * @cfg {Object} propertyNames map of property Names to their displayed value
56499          */
56500     
56501     render : function(){
56502         Roo.grid.PropertyGrid.superclass.render.call(this);
56503         this.autoSize.defer(100, this);
56504     },
56505
56506     autoSize : function(){
56507         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56508         if(this.view){
56509             this.view.fitColumns();
56510         }
56511     },
56512
56513     onColumnResize : function(){
56514         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56515         this.autoSize();
56516     },
56517     /**
56518      * Sets the data for the Grid
56519      * accepts a Key => Value object of all the elements avaiable.
56520      * @param {Object} data  to appear in grid.
56521      */
56522     setSource : function(source){
56523         this.store.setSource(source);
56524         //this.autoSize();
56525     },
56526     /**
56527      * Gets all the data from the grid.
56528      * @return {Object} data  data stored in grid
56529      */
56530     getSource : function(){
56531         return this.store.getSource();
56532     }
56533 });/*
56534   
56535  * Licence LGPL
56536  
56537  */
56538  
56539 /**
56540  * @class Roo.grid.Calendar
56541  * @extends Roo.util.Grid
56542  * This class extends the Grid to provide a calendar widget
56543  * <br><br>Usage:<pre><code>
56544  var grid = new Roo.grid.Calendar("my-container-id", {
56545      ds: myDataStore,
56546      cm: myColModel,
56547      selModel: mySelectionModel,
56548      autoSizeColumns: true,
56549      monitorWindowResize: false,
56550      trackMouseOver: true
56551      eventstore : real data store..
56552  });
56553  // set any options
56554  grid.render();
56555   
56556   * @constructor
56557  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56558  * The container MUST have some type of size defined for the grid to fill. The container will be
56559  * automatically set to position relative if it isn't already.
56560  * @param {Object} config A config object that sets properties on this grid.
56561  */
56562 Roo.grid.Calendar = function(container, config){
56563         // initialize the container
56564         this.container = Roo.get(container);
56565         this.container.update("");
56566         this.container.setStyle("overflow", "hidden");
56567     this.container.addClass('x-grid-container');
56568
56569     this.id = this.container.id;
56570
56571     Roo.apply(this, config);
56572     // check and correct shorthanded configs
56573     
56574     var rows = [];
56575     var d =1;
56576     for (var r = 0;r < 6;r++) {
56577         
56578         rows[r]=[];
56579         for (var c =0;c < 7;c++) {
56580             rows[r][c]= '';
56581         }
56582     }
56583     if (this.eventStore) {
56584         this.eventStore= Roo.factory(this.eventStore, Roo.data);
56585         this.eventStore.on('load',this.onLoad, this);
56586         this.eventStore.on('beforeload',this.clearEvents, this);
56587          
56588     }
56589     
56590     this.dataSource = new Roo.data.Store({
56591             proxy: new Roo.data.MemoryProxy(rows),
56592             reader: new Roo.data.ArrayReader({}, [
56593                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
56594     });
56595
56596     this.dataSource.load();
56597     this.ds = this.dataSource;
56598     this.ds.xmodule = this.xmodule || false;
56599     
56600     
56601     var cellRender = function(v,x,r)
56602     {
56603         return String.format(
56604             '<div class="fc-day  fc-widget-content"><div>' +
56605                 '<div class="fc-event-container"></div>' +
56606                 '<div class="fc-day-number">{0}</div>'+
56607                 
56608                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
56609             '</div></div>', v);
56610     
56611     }
56612     
56613     
56614     this.colModel = new Roo.grid.ColumnModel( [
56615         {
56616             xtype: 'ColumnModel',
56617             xns: Roo.grid,
56618             dataIndex : 'weekday0',
56619             header : 'Sunday',
56620             renderer : cellRender
56621         },
56622         {
56623             xtype: 'ColumnModel',
56624             xns: Roo.grid,
56625             dataIndex : 'weekday1',
56626             header : 'Monday',
56627             renderer : cellRender
56628         },
56629         {
56630             xtype: 'ColumnModel',
56631             xns: Roo.grid,
56632             dataIndex : 'weekday2',
56633             header : 'Tuesday',
56634             renderer : cellRender
56635         },
56636         {
56637             xtype: 'ColumnModel',
56638             xns: Roo.grid,
56639             dataIndex : 'weekday3',
56640             header : 'Wednesday',
56641             renderer : cellRender
56642         },
56643         {
56644             xtype: 'ColumnModel',
56645             xns: Roo.grid,
56646             dataIndex : 'weekday4',
56647             header : 'Thursday',
56648             renderer : cellRender
56649         },
56650         {
56651             xtype: 'ColumnModel',
56652             xns: Roo.grid,
56653             dataIndex : 'weekday5',
56654             header : 'Friday',
56655             renderer : cellRender
56656         },
56657         {
56658             xtype: 'ColumnModel',
56659             xns: Roo.grid,
56660             dataIndex : 'weekday6',
56661             header : 'Saturday',
56662             renderer : cellRender
56663         }
56664     ]);
56665     this.cm = this.colModel;
56666     this.cm.xmodule = this.xmodule || false;
56667  
56668         
56669           
56670     //this.selModel = new Roo.grid.CellSelectionModel();
56671     //this.sm = this.selModel;
56672     //this.selModel.init(this);
56673     
56674     
56675     if(this.width){
56676         this.container.setWidth(this.width);
56677     }
56678
56679     if(this.height){
56680         this.container.setHeight(this.height);
56681     }
56682     /** @private */
56683         this.addEvents({
56684         // raw events
56685         /**
56686          * @event click
56687          * The raw click event for the entire grid.
56688          * @param {Roo.EventObject} e
56689          */
56690         "click" : true,
56691         /**
56692          * @event dblclick
56693          * The raw dblclick event for the entire grid.
56694          * @param {Roo.EventObject} e
56695          */
56696         "dblclick" : true,
56697         /**
56698          * @event contextmenu
56699          * The raw contextmenu event for the entire grid.
56700          * @param {Roo.EventObject} e
56701          */
56702         "contextmenu" : true,
56703         /**
56704          * @event mousedown
56705          * The raw mousedown event for the entire grid.
56706          * @param {Roo.EventObject} e
56707          */
56708         "mousedown" : true,
56709         /**
56710          * @event mouseup
56711          * The raw mouseup event for the entire grid.
56712          * @param {Roo.EventObject} e
56713          */
56714         "mouseup" : true,
56715         /**
56716          * @event mouseover
56717          * The raw mouseover event for the entire grid.
56718          * @param {Roo.EventObject} e
56719          */
56720         "mouseover" : true,
56721         /**
56722          * @event mouseout
56723          * The raw mouseout event for the entire grid.
56724          * @param {Roo.EventObject} e
56725          */
56726         "mouseout" : true,
56727         /**
56728          * @event keypress
56729          * The raw keypress event for the entire grid.
56730          * @param {Roo.EventObject} e
56731          */
56732         "keypress" : true,
56733         /**
56734          * @event keydown
56735          * The raw keydown event for the entire grid.
56736          * @param {Roo.EventObject} e
56737          */
56738         "keydown" : true,
56739
56740         // custom events
56741
56742         /**
56743          * @event cellclick
56744          * Fires when a cell is clicked
56745          * @param {Grid} this
56746          * @param {Number} rowIndex
56747          * @param {Number} columnIndex
56748          * @param {Roo.EventObject} e
56749          */
56750         "cellclick" : true,
56751         /**
56752          * @event celldblclick
56753          * Fires when a cell is double clicked
56754          * @param {Grid} this
56755          * @param {Number} rowIndex
56756          * @param {Number} columnIndex
56757          * @param {Roo.EventObject} e
56758          */
56759         "celldblclick" : true,
56760         /**
56761          * @event rowclick
56762          * Fires when a row is clicked
56763          * @param {Grid} this
56764          * @param {Number} rowIndex
56765          * @param {Roo.EventObject} e
56766          */
56767         "rowclick" : true,
56768         /**
56769          * @event rowdblclick
56770          * Fires when a row is double clicked
56771          * @param {Grid} this
56772          * @param {Number} rowIndex
56773          * @param {Roo.EventObject} e
56774          */
56775         "rowdblclick" : true,
56776         /**
56777          * @event headerclick
56778          * Fires when a header is clicked
56779          * @param {Grid} this
56780          * @param {Number} columnIndex
56781          * @param {Roo.EventObject} e
56782          */
56783         "headerclick" : true,
56784         /**
56785          * @event headerdblclick
56786          * Fires when a header cell is double clicked
56787          * @param {Grid} this
56788          * @param {Number} columnIndex
56789          * @param {Roo.EventObject} e
56790          */
56791         "headerdblclick" : true,
56792         /**
56793          * @event rowcontextmenu
56794          * Fires when a row is right clicked
56795          * @param {Grid} this
56796          * @param {Number} rowIndex
56797          * @param {Roo.EventObject} e
56798          */
56799         "rowcontextmenu" : true,
56800         /**
56801          * @event cellcontextmenu
56802          * Fires when a cell is right clicked
56803          * @param {Grid} this
56804          * @param {Number} rowIndex
56805          * @param {Number} cellIndex
56806          * @param {Roo.EventObject} e
56807          */
56808          "cellcontextmenu" : true,
56809         /**
56810          * @event headercontextmenu
56811          * Fires when a header is right clicked
56812          * @param {Grid} this
56813          * @param {Number} columnIndex
56814          * @param {Roo.EventObject} e
56815          */
56816         "headercontextmenu" : true,
56817         /**
56818          * @event bodyscroll
56819          * Fires when the body element is scrolled
56820          * @param {Number} scrollLeft
56821          * @param {Number} scrollTop
56822          */
56823         "bodyscroll" : true,
56824         /**
56825          * @event columnresize
56826          * Fires when the user resizes a column
56827          * @param {Number} columnIndex
56828          * @param {Number} newSize
56829          */
56830         "columnresize" : true,
56831         /**
56832          * @event columnmove
56833          * Fires when the user moves a column
56834          * @param {Number} oldIndex
56835          * @param {Number} newIndex
56836          */
56837         "columnmove" : true,
56838         /**
56839          * @event startdrag
56840          * Fires when row(s) start being dragged
56841          * @param {Grid} this
56842          * @param {Roo.GridDD} dd The drag drop object
56843          * @param {event} e The raw browser event
56844          */
56845         "startdrag" : true,
56846         /**
56847          * @event enddrag
56848          * Fires when a drag operation is complete
56849          * @param {Grid} this
56850          * @param {Roo.GridDD} dd The drag drop object
56851          * @param {event} e The raw browser event
56852          */
56853         "enddrag" : true,
56854         /**
56855          * @event dragdrop
56856          * Fires when dragged row(s) are dropped on a valid DD target
56857          * @param {Grid} this
56858          * @param {Roo.GridDD} dd The drag drop object
56859          * @param {String} targetId The target drag drop object
56860          * @param {event} e The raw browser event
56861          */
56862         "dragdrop" : true,
56863         /**
56864          * @event dragover
56865          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56866          * @param {Grid} this
56867          * @param {Roo.GridDD} dd The drag drop object
56868          * @param {String} targetId The target drag drop object
56869          * @param {event} e The raw browser event
56870          */
56871         "dragover" : true,
56872         /**
56873          * @event dragenter
56874          *  Fires when the dragged row(s) first cross another DD target while being dragged
56875          * @param {Grid} this
56876          * @param {Roo.GridDD} dd The drag drop object
56877          * @param {String} targetId The target drag drop object
56878          * @param {event} e The raw browser event
56879          */
56880         "dragenter" : true,
56881         /**
56882          * @event dragout
56883          * Fires when the dragged row(s) leave another DD target while being dragged
56884          * @param {Grid} this
56885          * @param {Roo.GridDD} dd The drag drop object
56886          * @param {String} targetId The target drag drop object
56887          * @param {event} e The raw browser event
56888          */
56889         "dragout" : true,
56890         /**
56891          * @event rowclass
56892          * Fires when a row is rendered, so you can change add a style to it.
56893          * @param {GridView} gridview   The grid view
56894          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56895          */
56896         'rowclass' : true,
56897
56898         /**
56899          * @event render
56900          * Fires when the grid is rendered
56901          * @param {Grid} grid
56902          */
56903         'render' : true,
56904             /**
56905              * @event select
56906              * Fires when a date is selected
56907              * @param {DatePicker} this
56908              * @param {Date} date The selected date
56909              */
56910         'select': true,
56911         /**
56912              * @event monthchange
56913              * Fires when the displayed month changes 
56914              * @param {DatePicker} this
56915              * @param {Date} date The selected month
56916              */
56917         'monthchange': true,
56918         /**
56919              * @event evententer
56920              * Fires when mouse over an event
56921              * @param {Calendar} this
56922              * @param {event} Event
56923              */
56924         'evententer': true,
56925         /**
56926              * @event eventleave
56927              * Fires when the mouse leaves an
56928              * @param {Calendar} this
56929              * @param {event}
56930              */
56931         'eventleave': true,
56932         /**
56933              * @event eventclick
56934              * Fires when the mouse click an
56935              * @param {Calendar} this
56936              * @param {event}
56937              */
56938         'eventclick': true,
56939         /**
56940              * @event eventrender
56941              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
56942              * @param {Calendar} this
56943              * @param {data} data to be modified
56944              */
56945         'eventrender': true
56946         
56947     });
56948
56949     Roo.grid.Grid.superclass.constructor.call(this);
56950     this.on('render', function() {
56951         this.view.el.addClass('x-grid-cal'); 
56952         
56953         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
56954
56955     },this);
56956     
56957     if (!Roo.grid.Calendar.style) {
56958         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
56959             
56960             
56961             '.x-grid-cal .x-grid-col' :  {
56962                 height: 'auto !important',
56963                 'vertical-align': 'top'
56964             },
56965             '.x-grid-cal  .fc-event-hori' : {
56966                 height: '14px'
56967             }
56968              
56969             
56970         }, Roo.id());
56971     }
56972
56973     
56974     
56975 };
56976 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
56977     /**
56978      * @cfg {Store} eventStore The store that loads events.
56979      */
56980     eventStore : 25,
56981
56982      
56983     activeDate : false,
56984     startDay : 0,
56985     autoWidth : true,
56986     monitorWindowResize : false,
56987
56988     
56989     resizeColumns : function() {
56990         var col = (this.view.el.getWidth() / 7) - 3;
56991         // loop through cols, and setWidth
56992         for(var i =0 ; i < 7 ; i++){
56993             this.cm.setColumnWidth(i, col);
56994         }
56995     },
56996      setDate :function(date) {
56997         
56998         Roo.log('setDate?');
56999         
57000         this.resizeColumns();
57001         var vd = this.activeDate;
57002         this.activeDate = date;
57003 //        if(vd && this.el){
57004 //            var t = date.getTime();
57005 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57006 //                Roo.log('using add remove');
57007 //                
57008 //                this.fireEvent('monthchange', this, date);
57009 //                
57010 //                this.cells.removeClass("fc-state-highlight");
57011 //                this.cells.each(function(c){
57012 //                   if(c.dateValue == t){
57013 //                       c.addClass("fc-state-highlight");
57014 //                       setTimeout(function(){
57015 //                            try{c.dom.firstChild.focus();}catch(e){}
57016 //                       }, 50);
57017 //                       return false;
57018 //                   }
57019 //                   return true;
57020 //                });
57021 //                return;
57022 //            }
57023 //        }
57024         
57025         var days = date.getDaysInMonth();
57026         
57027         var firstOfMonth = date.getFirstDateOfMonth();
57028         var startingPos = firstOfMonth.getDay()-this.startDay;
57029         
57030         if(startingPos < this.startDay){
57031             startingPos += 7;
57032         }
57033         
57034         var pm = date.add(Date.MONTH, -1);
57035         var prevStart = pm.getDaysInMonth()-startingPos;
57036 //        
57037         
57038         
57039         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57040         
57041         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57042         //this.cells.addClassOnOver('fc-state-hover');
57043         
57044         var cells = this.cells.elements;
57045         var textEls = this.textNodes;
57046         
57047         //Roo.each(cells, function(cell){
57048         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57049         //});
57050         
57051         days += startingPos;
57052
57053         // convert everything to numbers so it's fast
57054         var day = 86400000;
57055         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57056         //Roo.log(d);
57057         //Roo.log(pm);
57058         //Roo.log(prevStart);
57059         
57060         var today = new Date().clearTime().getTime();
57061         var sel = date.clearTime().getTime();
57062         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57063         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57064         var ddMatch = this.disabledDatesRE;
57065         var ddText = this.disabledDatesText;
57066         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57067         var ddaysText = this.disabledDaysText;
57068         var format = this.format;
57069         
57070         var setCellClass = function(cal, cell){
57071             
57072             //Roo.log('set Cell Class');
57073             cell.title = "";
57074             var t = d.getTime();
57075             
57076             //Roo.log(d);
57077             
57078             
57079             cell.dateValue = t;
57080             if(t == today){
57081                 cell.className += " fc-today";
57082                 cell.className += " fc-state-highlight";
57083                 cell.title = cal.todayText;
57084             }
57085             if(t == sel){
57086                 // disable highlight in other month..
57087                 cell.className += " fc-state-highlight";
57088                 
57089             }
57090             // disabling
57091             if(t < min) {
57092                 //cell.className = " fc-state-disabled";
57093                 cell.title = cal.minText;
57094                 return;
57095             }
57096             if(t > max) {
57097                 //cell.className = " fc-state-disabled";
57098                 cell.title = cal.maxText;
57099                 return;
57100             }
57101             if(ddays){
57102                 if(ddays.indexOf(d.getDay()) != -1){
57103                     // cell.title = ddaysText;
57104                    // cell.className = " fc-state-disabled";
57105                 }
57106             }
57107             if(ddMatch && format){
57108                 var fvalue = d.dateFormat(format);
57109                 if(ddMatch.test(fvalue)){
57110                     cell.title = ddText.replace("%0", fvalue);
57111                    cell.className = " fc-state-disabled";
57112                 }
57113             }
57114             
57115             if (!cell.initialClassName) {
57116                 cell.initialClassName = cell.dom.className;
57117             }
57118             
57119             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57120         };
57121
57122         var i = 0;
57123         
57124         for(; i < startingPos; i++) {
57125             cells[i].dayName =  (++prevStart);
57126             Roo.log(textEls[i]);
57127             d.setDate(d.getDate()+1);
57128             
57129             //cells[i].className = "fc-past fc-other-month";
57130             setCellClass(this, cells[i]);
57131         }
57132         
57133         var intDay = 0;
57134         
57135         for(; i < days; i++){
57136             intDay = i - startingPos + 1;
57137             cells[i].dayName =  (intDay);
57138             d.setDate(d.getDate()+1);
57139             
57140             cells[i].className = ''; // "x-date-active";
57141             setCellClass(this, cells[i]);
57142         }
57143         var extraDays = 0;
57144         
57145         for(; i < 42; i++) {
57146             //textEls[i].innerHTML = (++extraDays);
57147             
57148             d.setDate(d.getDate()+1);
57149             cells[i].dayName = (++extraDays);
57150             cells[i].className = "fc-future fc-other-month";
57151             setCellClass(this, cells[i]);
57152         }
57153         
57154         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57155         
57156         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57157         
57158         // this will cause all the cells to mis
57159         var rows= [];
57160         var i =0;
57161         for (var r = 0;r < 6;r++) {
57162             for (var c =0;c < 7;c++) {
57163                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57164             }    
57165         }
57166         
57167         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57168         for(i=0;i<cells.length;i++) {
57169             
57170             this.cells.elements[i].dayName = cells[i].dayName ;
57171             this.cells.elements[i].className = cells[i].className;
57172             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57173             this.cells.elements[i].title = cells[i].title ;
57174             this.cells.elements[i].dateValue = cells[i].dateValue ;
57175         }
57176         
57177         
57178         
57179         
57180         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57181         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57182         
57183         ////if(totalRows != 6){
57184             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57185            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57186        // }
57187         
57188         this.fireEvent('monthchange', this, date);
57189         
57190         
57191     },
57192  /**
57193      * Returns the grid's SelectionModel.
57194      * @return {SelectionModel}
57195      */
57196     getSelectionModel : function(){
57197         if(!this.selModel){
57198             this.selModel = new Roo.grid.CellSelectionModel();
57199         }
57200         return this.selModel;
57201     },
57202
57203     load: function() {
57204         this.eventStore.load()
57205         
57206         
57207         
57208     },
57209     
57210     findCell : function(dt) {
57211         dt = dt.clearTime().getTime();
57212         var ret = false;
57213         this.cells.each(function(c){
57214             //Roo.log("check " +c.dateValue + '?=' + dt);
57215             if(c.dateValue == dt){
57216                 ret = c;
57217                 return false;
57218             }
57219             return true;
57220         });
57221         
57222         return ret;
57223     },
57224     
57225     findCells : function(rec) {
57226         var s = rec.data.start_dt.clone().clearTime().getTime();
57227        // Roo.log(s);
57228         var e= rec.data.end_dt.clone().clearTime().getTime();
57229        // Roo.log(e);
57230         var ret = [];
57231         this.cells.each(function(c){
57232              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57233             
57234             if(c.dateValue > e){
57235                 return ;
57236             }
57237             if(c.dateValue < s){
57238                 return ;
57239             }
57240             ret.push(c);
57241         });
57242         
57243         return ret;    
57244     },
57245     
57246     findBestRow: function(cells)
57247     {
57248         var ret = 0;
57249         
57250         for (var i =0 ; i < cells.length;i++) {
57251             ret  = Math.max(cells[i].rows || 0,ret);
57252         }
57253         return ret;
57254         
57255     },
57256     
57257     
57258     addItem : function(rec)
57259     {
57260         // look for vertical location slot in
57261         var cells = this.findCells(rec);
57262         
57263         rec.row = this.findBestRow(cells);
57264         
57265         // work out the location.
57266         
57267         var crow = false;
57268         var rows = [];
57269         for(var i =0; i < cells.length; i++) {
57270             if (!crow) {
57271                 crow = {
57272                     start : cells[i],
57273                     end :  cells[i]
57274                 };
57275                 continue;
57276             }
57277             if (crow.start.getY() == cells[i].getY()) {
57278                 // on same row.
57279                 crow.end = cells[i];
57280                 continue;
57281             }
57282             // different row.
57283             rows.push(crow);
57284             crow = {
57285                 start: cells[i],
57286                 end : cells[i]
57287             };
57288             
57289         }
57290         
57291         rows.push(crow);
57292         rec.els = [];
57293         rec.rows = rows;
57294         rec.cells = cells;
57295         for (var i = 0; i < cells.length;i++) {
57296             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57297             
57298         }
57299         
57300         
57301     },
57302     
57303     clearEvents: function() {
57304         
57305         if (!this.eventStore.getCount()) {
57306             return;
57307         }
57308         // reset number of rows in cells.
57309         Roo.each(this.cells.elements, function(c){
57310             c.rows = 0;
57311         });
57312         
57313         this.eventStore.each(function(e) {
57314             this.clearEvent(e);
57315         },this);
57316         
57317     },
57318     
57319     clearEvent : function(ev)
57320     {
57321         if (ev.els) {
57322             Roo.each(ev.els, function(el) {
57323                 el.un('mouseenter' ,this.onEventEnter, this);
57324                 el.un('mouseleave' ,this.onEventLeave, this);
57325                 el.remove();
57326             },this);
57327             ev.els = [];
57328         }
57329     },
57330     
57331     
57332     renderEvent : function(ev,ctr) {
57333         if (!ctr) {
57334              ctr = this.view.el.select('.fc-event-container',true).first();
57335         }
57336         
57337          
57338         this.clearEvent(ev);
57339             //code
57340        
57341         
57342         
57343         ev.els = [];
57344         var cells = ev.cells;
57345         var rows = ev.rows;
57346         this.fireEvent('eventrender', this, ev);
57347         
57348         for(var i =0; i < rows.length; i++) {
57349             
57350             cls = '';
57351             if (i == 0) {
57352                 cls += ' fc-event-start';
57353             }
57354             if ((i+1) == rows.length) {
57355                 cls += ' fc-event-end';
57356             }
57357             
57358             //Roo.log(ev.data);
57359             // how many rows should it span..
57360             var cg = this.eventTmpl.append(ctr,Roo.apply({
57361                 fccls : cls
57362                 
57363             }, ev.data) , true);
57364             
57365             
57366             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57367             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57368             cg.on('click', this.onEventClick, this, ev);
57369             
57370             ev.els.push(cg);
57371             
57372             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57373             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57374             //Roo.log(cg);
57375              
57376             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57377             cg.setWidth(ebox.right - sbox.x -2);
57378         }
57379     },
57380     
57381     renderEvents: function()
57382     {   
57383         // first make sure there is enough space..
57384         
57385         if (!this.eventTmpl) {
57386             this.eventTmpl = new Roo.Template(
57387                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57388                     '<div class="fc-event-inner">' +
57389                         '<span class="fc-event-time">{time}</span>' +
57390                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57391                     '</div>' +
57392                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57393                 '</div>'
57394             );
57395                 
57396         }
57397                
57398         
57399         
57400         this.cells.each(function(c) {
57401             //Roo.log(c.select('.fc-day-content div',true).first());
57402             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57403         });
57404         
57405         var ctr = this.view.el.select('.fc-event-container',true).first();
57406         
57407         var cls;
57408         this.eventStore.each(function(ev){
57409             
57410             this.renderEvent(ev);
57411              
57412              
57413         }, this);
57414         this.view.layout();
57415         
57416     },
57417     
57418     onEventEnter: function (e, el,event,d) {
57419         this.fireEvent('evententer', this, el, event);
57420     },
57421     
57422     onEventLeave: function (e, el,event,d) {
57423         this.fireEvent('eventleave', this, el, event);
57424     },
57425     
57426     onEventClick: function (e, el,event,d) {
57427         this.fireEvent('eventclick', this, el, event);
57428     },
57429     
57430     onMonthChange: function () {
57431         this.store.load();
57432     },
57433     
57434     onLoad: function () {
57435         
57436         //Roo.log('calendar onload');
57437 //         
57438         if(this.eventStore.getCount() > 0){
57439             
57440            
57441             
57442             this.eventStore.each(function(d){
57443                 
57444                 
57445                 // FIXME..
57446                 var add =   d.data;
57447                 if (typeof(add.end_dt) == 'undefined')  {
57448                     Roo.log("Missing End time in calendar data: ");
57449                     Roo.log(d);
57450                     return;
57451                 }
57452                 if (typeof(add.start_dt) == 'undefined')  {
57453                     Roo.log("Missing Start time in calendar data: ");
57454                     Roo.log(d);
57455                     return;
57456                 }
57457                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57458                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57459                 add.id = add.id || d.id;
57460                 add.title = add.title || '??';
57461                 
57462                 this.addItem(d);
57463                 
57464              
57465             },this);
57466         }
57467         
57468         this.renderEvents();
57469     }
57470     
57471
57472 });
57473 /*
57474  grid : {
57475                 xtype: 'Grid',
57476                 xns: Roo.grid,
57477                 listeners : {
57478                     render : function ()
57479                     {
57480                         _this.grid = this;
57481                         
57482                         if (!this.view.el.hasClass('course-timesheet')) {
57483                             this.view.el.addClass('course-timesheet');
57484                         }
57485                         if (this.tsStyle) {
57486                             this.ds.load({});
57487                             return; 
57488                         }
57489                         Roo.log('width');
57490                         Roo.log(_this.grid.view.el.getWidth());
57491                         
57492                         
57493                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57494                             '.course-timesheet .x-grid-row' : {
57495                                 height: '80px'
57496                             },
57497                             '.x-grid-row td' : {
57498                                 'vertical-align' : 0
57499                             },
57500                             '.course-edit-link' : {
57501                                 'color' : 'blue',
57502                                 'text-overflow' : 'ellipsis',
57503                                 'overflow' : 'hidden',
57504                                 'white-space' : 'nowrap',
57505                                 'cursor' : 'pointer'
57506                             },
57507                             '.sub-link' : {
57508                                 'color' : 'green'
57509                             },
57510                             '.de-act-sup-link' : {
57511                                 'color' : 'purple',
57512                                 'text-decoration' : 'line-through'
57513                             },
57514                             '.de-act-link' : {
57515                                 'color' : 'red',
57516                                 'text-decoration' : 'line-through'
57517                             },
57518                             '.course-timesheet .course-highlight' : {
57519                                 'border-top-style': 'dashed !important',
57520                                 'border-bottom-bottom': 'dashed !important'
57521                             },
57522                             '.course-timesheet .course-item' : {
57523                                 'font-family'   : 'tahoma, arial, helvetica',
57524                                 'font-size'     : '11px',
57525                                 'overflow'      : 'hidden',
57526                                 'padding-left'  : '10px',
57527                                 'padding-right' : '10px',
57528                                 'padding-top' : '10px' 
57529                             }
57530                             
57531                         }, Roo.id());
57532                                 this.ds.load({});
57533                     }
57534                 },
57535                 autoWidth : true,
57536                 monitorWindowResize : false,
57537                 cellrenderer : function(v,x,r)
57538                 {
57539                     return v;
57540                 },
57541                 sm : {
57542                     xtype: 'CellSelectionModel',
57543                     xns: Roo.grid
57544                 },
57545                 dataSource : {
57546                     xtype: 'Store',
57547                     xns: Roo.data,
57548                     listeners : {
57549                         beforeload : function (_self, options)
57550                         {
57551                             options.params = options.params || {};
57552                             options.params._month = _this.monthField.getValue();
57553                             options.params.limit = 9999;
57554                             options.params['sort'] = 'when_dt';    
57555                             options.params['dir'] = 'ASC';    
57556                             this.proxy.loadResponse = this.loadResponse;
57557                             Roo.log("load?");
57558                             //this.addColumns();
57559                         },
57560                         load : function (_self, records, options)
57561                         {
57562                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
57563                                 // if you click on the translation.. you can edit it...
57564                                 var el = Roo.get(this);
57565                                 var id = el.dom.getAttribute('data-id');
57566                                 var d = el.dom.getAttribute('data-date');
57567                                 var t = el.dom.getAttribute('data-time');
57568                                 //var id = this.child('span').dom.textContent;
57569                                 
57570                                 //Roo.log(this);
57571                                 Pman.Dialog.CourseCalendar.show({
57572                                     id : id,
57573                                     when_d : d,
57574                                     when_t : t,
57575                                     productitem_active : id ? 1 : 0
57576                                 }, function() {
57577                                     _this.grid.ds.load({});
57578                                 });
57579                            
57580                            });
57581                            
57582                            _this.panel.fireEvent('resize', [ '', '' ]);
57583                         }
57584                     },
57585                     loadResponse : function(o, success, response){
57586                             // this is overridden on before load..
57587                             
57588                             Roo.log("our code?");       
57589                             //Roo.log(success);
57590                             //Roo.log(response)
57591                             delete this.activeRequest;
57592                             if(!success){
57593                                 this.fireEvent("loadexception", this, o, response);
57594                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57595                                 return;
57596                             }
57597                             var result;
57598                             try {
57599                                 result = o.reader.read(response);
57600                             }catch(e){
57601                                 Roo.log("load exception?");
57602                                 this.fireEvent("loadexception", this, o, response, e);
57603                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57604                                 return;
57605                             }
57606                             Roo.log("ready...");        
57607                             // loop through result.records;
57608                             // and set this.tdate[date] = [] << array of records..
57609                             _this.tdata  = {};
57610                             Roo.each(result.records, function(r){
57611                                 //Roo.log(r.data);
57612                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
57613                                     _this.tdata[r.data.when_dt.format('j')] = [];
57614                                 }
57615                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
57616                             });
57617                             
57618                             //Roo.log(_this.tdata);
57619                             
57620                             result.records = [];
57621                             result.totalRecords = 6;
57622                     
57623                             // let's generate some duumy records for the rows.
57624                             //var st = _this.dateField.getValue();
57625                             
57626                             // work out monday..
57627                             //st = st.add(Date.DAY, -1 * st.format('w'));
57628                             
57629                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57630                             
57631                             var firstOfMonth = date.getFirstDayOfMonth();
57632                             var days = date.getDaysInMonth();
57633                             var d = 1;
57634                             var firstAdded = false;
57635                             for (var i = 0; i < result.totalRecords ; i++) {
57636                                 //var d= st.add(Date.DAY, i);
57637                                 var row = {};
57638                                 var added = 0;
57639                                 for(var w = 0 ; w < 7 ; w++){
57640                                     if(!firstAdded && firstOfMonth != w){
57641                                         continue;
57642                                     }
57643                                     if(d > days){
57644                                         continue;
57645                                     }
57646                                     firstAdded = true;
57647                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
57648                                     row['weekday'+w] = String.format(
57649                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
57650                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
57651                                                     d,
57652                                                     date.format('Y-m-')+dd
57653                                                 );
57654                                     added++;
57655                                     if(typeof(_this.tdata[d]) != 'undefined'){
57656                                         Roo.each(_this.tdata[d], function(r){
57657                                             var is_sub = '';
57658                                             var deactive = '';
57659                                             var id = r.id;
57660                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
57661                                             if(r.parent_id*1>0){
57662                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
57663                                                 id = r.parent_id;
57664                                             }
57665                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
57666                                                 deactive = 'de-act-link';
57667                                             }
57668                                             
57669                                             row['weekday'+w] += String.format(
57670                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
57671                                                     id, //0
57672                                                     r.product_id_name, //1
57673                                                     r.when_dt.format('h:ia'), //2
57674                                                     is_sub, //3
57675                                                     deactive, //4
57676                                                     desc // 5
57677                                             );
57678                                         });
57679                                     }
57680                                     d++;
57681                                 }
57682                                 
57683                                 // only do this if something added..
57684                                 if(added > 0){ 
57685                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
57686                                 }
57687                                 
57688                                 
57689                                 // push it twice. (second one with an hour..
57690                                 
57691                             }
57692                             //Roo.log(result);
57693                             this.fireEvent("load", this, o, o.request.arg);
57694                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
57695                         },
57696                     sortInfo : {field: 'when_dt', direction : 'ASC' },
57697                     proxy : {
57698                         xtype: 'HttpProxy',
57699                         xns: Roo.data,
57700                         method : 'GET',
57701                         url : baseURL + '/Roo/Shop_course.php'
57702                     },
57703                     reader : {
57704                         xtype: 'JsonReader',
57705                         xns: Roo.data,
57706                         id : 'id',
57707                         fields : [
57708                             {
57709                                 'name': 'id',
57710                                 'type': 'int'
57711                             },
57712                             {
57713                                 'name': 'when_dt',
57714                                 'type': 'string'
57715                             },
57716                             {
57717                                 'name': 'end_dt',
57718                                 'type': 'string'
57719                             },
57720                             {
57721                                 'name': 'parent_id',
57722                                 'type': 'int'
57723                             },
57724                             {
57725                                 'name': 'product_id',
57726                                 'type': 'int'
57727                             },
57728                             {
57729                                 'name': 'productitem_id',
57730                                 'type': 'int'
57731                             },
57732                             {
57733                                 'name': 'guid',
57734                                 'type': 'int'
57735                             }
57736                         ]
57737                     }
57738                 },
57739                 toolbar : {
57740                     xtype: 'Toolbar',
57741                     xns: Roo,
57742                     items : [
57743                         {
57744                             xtype: 'Button',
57745                             xns: Roo.Toolbar,
57746                             listeners : {
57747                                 click : function (_self, e)
57748                                 {
57749                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57750                                     sd.setMonth(sd.getMonth()-1);
57751                                     _this.monthField.setValue(sd.format('Y-m-d'));
57752                                     _this.grid.ds.load({});
57753                                 }
57754                             },
57755                             text : "Back"
57756                         },
57757                         {
57758                             xtype: 'Separator',
57759                             xns: Roo.Toolbar
57760                         },
57761                         {
57762                             xtype: 'MonthField',
57763                             xns: Roo.form,
57764                             listeners : {
57765                                 render : function (_self)
57766                                 {
57767                                     _this.monthField = _self;
57768                                    // _this.monthField.set  today
57769                                 },
57770                                 select : function (combo, date)
57771                                 {
57772                                     _this.grid.ds.load({});
57773                                 }
57774                             },
57775                             value : (function() { return new Date(); })()
57776                         },
57777                         {
57778                             xtype: 'Separator',
57779                             xns: Roo.Toolbar
57780                         },
57781                         {
57782                             xtype: 'TextItem',
57783                             xns: Roo.Toolbar,
57784                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
57785                         },
57786                         {
57787                             xtype: 'Fill',
57788                             xns: Roo.Toolbar
57789                         },
57790                         {
57791                             xtype: 'Button',
57792                             xns: Roo.Toolbar,
57793                             listeners : {
57794                                 click : function (_self, e)
57795                                 {
57796                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57797                                     sd.setMonth(sd.getMonth()+1);
57798                                     _this.monthField.setValue(sd.format('Y-m-d'));
57799                                     _this.grid.ds.load({});
57800                                 }
57801                             },
57802                             text : "Next"
57803                         }
57804                     ]
57805                 },
57806                  
57807             }
57808         };
57809         
57810         *//*
57811  * Based on:
57812  * Ext JS Library 1.1.1
57813  * Copyright(c) 2006-2007, Ext JS, LLC.
57814  *
57815  * Originally Released Under LGPL - original licence link has changed is not relivant.
57816  *
57817  * Fork - LGPL
57818  * <script type="text/javascript">
57819  */
57820  
57821 /**
57822  * @class Roo.LoadMask
57823  * A simple utility class for generically masking elements while loading data.  If the element being masked has
57824  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
57825  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
57826  * element's UpdateManager load indicator and will be destroyed after the initial load.
57827  * @constructor
57828  * Create a new LoadMask
57829  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
57830  * @param {Object} config The config object
57831  */
57832 Roo.LoadMask = function(el, config){
57833     this.el = Roo.get(el);
57834     Roo.apply(this, config);
57835     if(this.store){
57836         this.store.on('beforeload', this.onBeforeLoad, this);
57837         this.store.on('load', this.onLoad, this);
57838         this.store.on('loadexception', this.onLoadException, this);
57839         this.removeMask = false;
57840     }else{
57841         var um = this.el.getUpdateManager();
57842         um.showLoadIndicator = false; // disable the default indicator
57843         um.on('beforeupdate', this.onBeforeLoad, this);
57844         um.on('update', this.onLoad, this);
57845         um.on('failure', this.onLoad, this);
57846         this.removeMask = true;
57847     }
57848 };
57849
57850 Roo.LoadMask.prototype = {
57851     /**
57852      * @cfg {Boolean} removeMask
57853      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
57854      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
57855      */
57856     /**
57857      * @cfg {String} msg
57858      * The text to display in a centered loading message box (defaults to 'Loading...')
57859      */
57860     msg : 'Loading...',
57861     /**
57862      * @cfg {String} msgCls
57863      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
57864      */
57865     msgCls : 'x-mask-loading',
57866
57867     /**
57868      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
57869      * @type Boolean
57870      */
57871     disabled: false,
57872
57873     /**
57874      * Disables the mask to prevent it from being displayed
57875      */
57876     disable : function(){
57877        this.disabled = true;
57878     },
57879
57880     /**
57881      * Enables the mask so that it can be displayed
57882      */
57883     enable : function(){
57884         this.disabled = false;
57885     },
57886     
57887     onLoadException : function()
57888     {
57889         Roo.log(arguments);
57890         
57891         if (typeof(arguments[3]) != 'undefined') {
57892             Roo.MessageBox.alert("Error loading",arguments[3]);
57893         } 
57894         /*
57895         try {
57896             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57897                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57898             }   
57899         } catch(e) {
57900             
57901         }
57902         */
57903     
57904         
57905         
57906         this.el.unmask(this.removeMask);
57907     },
57908     // private
57909     onLoad : function()
57910     {
57911         this.el.unmask(this.removeMask);
57912     },
57913
57914     // private
57915     onBeforeLoad : function(){
57916         if(!this.disabled){
57917             this.el.mask(this.msg, this.msgCls);
57918         }
57919     },
57920
57921     // private
57922     destroy : function(){
57923         if(this.store){
57924             this.store.un('beforeload', this.onBeforeLoad, this);
57925             this.store.un('load', this.onLoad, this);
57926             this.store.un('loadexception', this.onLoadException, this);
57927         }else{
57928             var um = this.el.getUpdateManager();
57929             um.un('beforeupdate', this.onBeforeLoad, this);
57930             um.un('update', this.onLoad, this);
57931             um.un('failure', this.onLoad, this);
57932         }
57933     }
57934 };/*
57935  * Based on:
57936  * Ext JS Library 1.1.1
57937  * Copyright(c) 2006-2007, Ext JS, LLC.
57938  *
57939  * Originally Released Under LGPL - original licence link has changed is not relivant.
57940  *
57941  * Fork - LGPL
57942  * <script type="text/javascript">
57943  */
57944
57945
57946 /**
57947  * @class Roo.XTemplate
57948  * @extends Roo.Template
57949  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
57950 <pre><code>
57951 var t = new Roo.XTemplate(
57952         '&lt;select name="{name}"&gt;',
57953                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
57954         '&lt;/select&gt;'
57955 );
57956  
57957 // then append, applying the master template values
57958  </code></pre>
57959  *
57960  * Supported features:
57961  *
57962  *  Tags:
57963
57964 <pre><code>
57965       {a_variable} - output encoded.
57966       {a_variable.format:("Y-m-d")} - call a method on the variable
57967       {a_variable:raw} - unencoded output
57968       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
57969       {a_variable:this.method_on_template(...)} - call a method on the template object.
57970  
57971 </code></pre>
57972  *  The tpl tag:
57973 <pre><code>
57974         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
57975         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
57976         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
57977         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
57978   
57979         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
57980         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
57981 </code></pre>
57982  *      
57983  */
57984 Roo.XTemplate = function()
57985 {
57986     Roo.XTemplate.superclass.constructor.apply(this, arguments);
57987     if (this.html) {
57988         this.compile();
57989     }
57990 };
57991
57992
57993 Roo.extend(Roo.XTemplate, Roo.Template, {
57994
57995     /**
57996      * The various sub templates
57997      */
57998     tpls : false,
57999     /**
58000      *
58001      * basic tag replacing syntax
58002      * WORD:WORD()
58003      *
58004      * // you can fake an object call by doing this
58005      *  x.t:(test,tesT) 
58006      * 
58007      */
58008     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58009
58010     /**
58011      * compile the template
58012      *
58013      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58014      *
58015      */
58016     compile: function()
58017     {
58018         var s = this.html;
58019      
58020         s = ['<tpl>', s, '</tpl>'].join('');
58021     
58022         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58023             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58024             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58025             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58026             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58027             m,
58028             id     = 0,
58029             tpls   = [];
58030     
58031         while(true == !!(m = s.match(re))){
58032             var forMatch   = m[0].match(nameRe),
58033                 ifMatch   = m[0].match(ifRe),
58034                 execMatch   = m[0].match(execRe),
58035                 namedMatch   = m[0].match(namedRe),
58036                 
58037                 exp  = null, 
58038                 fn   = null,
58039                 exec = null,
58040                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58041                 
58042             if (ifMatch) {
58043                 // if - puts fn into test..
58044                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58045                 if(exp){
58046                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58047                 }
58048             }
58049             
58050             if (execMatch) {
58051                 // exec - calls a function... returns empty if true is  returned.
58052                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58053                 if(exp){
58054                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58055                 }
58056             }
58057             
58058             
58059             if (name) {
58060                 // for = 
58061                 switch(name){
58062                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58063                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58064                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58065                 }
58066             }
58067             var uid = namedMatch ? namedMatch[1] : id;
58068             
58069             
58070             tpls.push({
58071                 id:     namedMatch ? namedMatch[1] : id,
58072                 target: name,
58073                 exec:   exec,
58074                 test:   fn,
58075                 body:   m[1] || ''
58076             });
58077             if (namedMatch) {
58078                 s = s.replace(m[0], '');
58079             } else { 
58080                 s = s.replace(m[0], '{xtpl'+ id + '}');
58081             }
58082             ++id;
58083         }
58084         this.tpls = [];
58085         for(var i = tpls.length-1; i >= 0; --i){
58086             this.compileTpl(tpls[i]);
58087             this.tpls[tpls[i].id] = tpls[i];
58088         }
58089         this.master = tpls[tpls.length-1];
58090         return this;
58091     },
58092     /**
58093      * same as applyTemplate, except it's done to one of the subTemplates
58094      * when using named templates, you can do:
58095      *
58096      * var str = pl.applySubTemplate('your-name', values);
58097      *
58098      * 
58099      * @param {Number} id of the template
58100      * @param {Object} values to apply to template
58101      * @param {Object} parent (normaly the instance of this object)
58102      */
58103     applySubTemplate : function(id, values, parent)
58104     {
58105         
58106         
58107         var t = this.tpls[id];
58108         
58109         
58110         try { 
58111             if(t.test && !t.test.call(this, values, parent)){
58112                 return '';
58113             }
58114         } catch(e) {
58115             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58116             Roo.log(e.toString());
58117             Roo.log(t.test);
58118             return ''
58119         }
58120         try { 
58121             
58122             if(t.exec && t.exec.call(this, values, parent)){
58123                 return '';
58124             }
58125         } catch(e) {
58126             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58127             Roo.log(e.toString());
58128             Roo.log(t.exec);
58129             return ''
58130         }
58131         try {
58132             var vs = t.target ? t.target.call(this, values, parent) : values;
58133             parent = t.target ? values : parent;
58134             if(t.target && vs instanceof Array){
58135                 var buf = [];
58136                 for(var i = 0, len = vs.length; i < len; i++){
58137                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58138                 }
58139                 return buf.join('');
58140             }
58141             return t.compiled.call(this, vs, parent);
58142         } catch (e) {
58143             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58144             Roo.log(e.toString());
58145             Roo.log(t.compiled);
58146             return '';
58147         }
58148     },
58149
58150     compileTpl : function(tpl)
58151     {
58152         var fm = Roo.util.Format;
58153         var useF = this.disableFormats !== true;
58154         var sep = Roo.isGecko ? "+" : ",";
58155         var undef = function(str) {
58156             Roo.log("Property not found :"  + str);
58157             return '';
58158         };
58159         
58160         var fn = function(m, name, format, args)
58161         {
58162             //Roo.log(arguments);
58163             args = args ? args.replace(/\\'/g,"'") : args;
58164             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58165             if (typeof(format) == 'undefined') {
58166                 format= 'htmlEncode';
58167             }
58168             if (format == 'raw' ) {
58169                 format = false;
58170             }
58171             
58172             if(name.substr(0, 4) == 'xtpl'){
58173                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58174             }
58175             
58176             // build an array of options to determine if value is undefined..
58177             
58178             // basically get 'xxxx.yyyy' then do
58179             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58180             //    (function () { Roo.log("Property not found"); return ''; })() :
58181             //    ......
58182             
58183             var udef_ar = [];
58184             var lookfor = '';
58185             Roo.each(name.split('.'), function(st) {
58186                 lookfor += (lookfor.length ? '.': '') + st;
58187                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58188             });
58189             
58190             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58191             
58192             
58193             if(format && useF){
58194                 
58195                 args = args ? ',' + args : "";
58196                  
58197                 if(format.substr(0, 5) != "this."){
58198                     format = "fm." + format + '(';
58199                 }else{
58200                     format = 'this.call("'+ format.substr(5) + '", ';
58201                     args = ", values";
58202                 }
58203                 
58204                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58205             }
58206              
58207             if (args.length) {
58208                 // called with xxyx.yuu:(test,test)
58209                 // change to ()
58210                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58211             }
58212             // raw.. - :raw modifier..
58213             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58214             
58215         };
58216         var body;
58217         // branched to use + in gecko and [].join() in others
58218         if(Roo.isGecko){
58219             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58220                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58221                     "';};};";
58222         }else{
58223             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58224             body.push(tpl.body.replace(/(\r\n|\n)/g,
58225                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58226             body.push("'].join('');};};");
58227             body = body.join('');
58228         }
58229         
58230         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58231        
58232         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58233         eval(body);
58234         
58235         return this;
58236     },
58237
58238     applyTemplate : function(values){
58239         return this.master.compiled.call(this, values, {});
58240         //var s = this.subs;
58241     },
58242
58243     apply : function(){
58244         return this.applyTemplate.apply(this, arguments);
58245     }
58246
58247  });
58248
58249 Roo.XTemplate.from = function(el){
58250     el = Roo.getDom(el);
58251     return new Roo.XTemplate(el.value || el.innerHTML);
58252 };