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         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1241         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1242         + "if (results && results.length > 0) {";
1243     var regex = "";
1244
1245     var special = false;
1246     var ch = '';
1247     for (var i = 0; i < format.length; ++i) {
1248         ch = format.charAt(i);
1249         if (!special && ch == "\\") {
1250             special = true;
1251         }
1252         else if (special) {
1253             special = false;
1254             regex += String.escape(ch);
1255         }
1256         else {
1257             var obj = Date.formatCodeToRegex(ch, currentGroup);
1258             currentGroup += obj.g;
1259             regex += obj.s;
1260             if (obj.g && obj.c) {
1261                 code += obj.c;
1262             }
1263         }
1264     }
1265
1266     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1267         + "{v = new Date(y, m, d, h, i, s);}\n"
1268         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1269         + "{v = new Date(y, m, d, h, i);}\n"
1270         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1271         + "{v = new Date(y, m, d, h);}\n"
1272         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1273         + "{v = new Date(y, m, d);}\n"
1274         + "else if (y >= 0 && m >= 0)\n"
1275         + "{v = new Date(y, m);}\n"
1276         + "else if (y >= 0)\n"
1277         + "{v = new Date(y);}\n"
1278         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1279         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1280         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1281         + ";}";
1282
1283     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1284     /** eval:var:zzzzzzzzzzzzz */
1285     eval(code);
1286 };
1287
1288 // private
1289 Date.formatCodeToRegex = function(character, currentGroup) {
1290     switch (character) {
1291     case "D":
1292         return {g:0,
1293         c:null,
1294         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1295     case "j":
1296         return {g:1,
1297             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1298             s:"(\\d{1,2})"}; // day of month without leading zeroes
1299     case "d":
1300         return {g:1,
1301             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1302             s:"(\\d{2})"}; // day of month with leading zeroes
1303     case "l":
1304         return {g:0,
1305             c:null,
1306             s:"(?:" + Date.dayNames.join("|") + ")"};
1307     case "S":
1308         return {g:0,
1309             c:null,
1310             s:"(?:st|nd|rd|th)"};
1311     case "w":
1312         return {g:0,
1313             c:null,
1314             s:"\\d"};
1315     case "z":
1316         return {g:0,
1317             c:null,
1318             s:"(?:\\d{1,3})"};
1319     case "W":
1320         return {g:0,
1321             c:null,
1322             s:"(?:\\d{2})"};
1323     case "F":
1324         return {g:1,
1325             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1326             s:"(" + Date.monthNames.join("|") + ")"};
1327     case "M":
1328         return {g:1,
1329             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1330             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1331     case "n":
1332         return {g:1,
1333             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1334             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1335     case "m":
1336         return {g:1,
1337             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1338             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1339     case "t":
1340         return {g:0,
1341             c:null,
1342             s:"\\d{1,2}"};
1343     case "L":
1344         return {g:0,
1345             c:null,
1346             s:"(?:1|0)"};
1347     case "Y":
1348         return {g:1,
1349             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1350             s:"(\\d{4})"};
1351     case "y":
1352         return {g:1,
1353             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1354                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1355             s:"(\\d{1,2})"};
1356     case "a":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'am') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(am|pm)"};
1362     case "A":
1363         return {g:1,
1364             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1365                 + "if (h == 12) { h = 0; }\n"
1366                 + "} else { if (h < 12) { h += 12; }}",
1367             s:"(AM|PM)"};
1368     case "g":
1369     case "G":
1370         return {g:1,
1371             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1372             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1373     case "h":
1374     case "H":
1375         return {g:1,
1376             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1377             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1378     case "i":
1379         return {g:1,
1380             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1381             s:"(\\d{2})"};
1382     case "s":
1383         return {g:1,
1384             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1385             s:"(\\d{2})"};
1386     case "O":
1387         return {g:1,
1388             c:[
1389                 "o = results[", currentGroup, "];\n",
1390                 "var sn = o.substring(0,1);\n", // get + / - sign
1391                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1392                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1393                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1394                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1395             ].join(""),
1396             s:"([+\-]\\d{2,4})"};
1397     
1398     
1399     case "P":
1400         return {g:1,
1401                 c:[
1402                    "o = results[", currentGroup, "];\n",
1403                    "var sn = o.substring(0,1);\n",
1404                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1405                    "var mn = o.substring(4,6) % 60;\n",
1406                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1407                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1408             ].join(""),
1409             s:"([+\-]\\d{4})"};
1410     case "T":
1411         return {g:0,
1412             c:null,
1413             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1414     case "Z":
1415         return {g:1,
1416             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1417                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1418             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1419     default:
1420         return {g:0,
1421             c:null,
1422             s:String.escape(character)};
1423     }
1424 };
1425
1426 /**
1427  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1428  * @return {String} The abbreviated timezone name (e.g. 'CST')
1429  */
1430 Date.prototype.getTimezone = function() {
1431     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1432 };
1433
1434 /**
1435  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1436  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1437  */
1438 Date.prototype.getGMTOffset = function() {
1439     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1440         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1441         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1442 };
1443
1444 /**
1445  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1446  * @return {String} 2-characters representing hours and 2-characters representing minutes
1447  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1448  */
1449 Date.prototype.getGMTColonOffset = function() {
1450         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1451                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1452                 + ":"
1453                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1454 }
1455
1456 /**
1457  * Get the numeric day number of the year, adjusted for leap year.
1458  * @return {Number} 0 through 364 (365 in leap years)
1459  */
1460 Date.prototype.getDayOfYear = function() {
1461     var num = 0;
1462     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1463     for (var i = 0; i < this.getMonth(); ++i) {
1464         num += Date.daysInMonth[i];
1465     }
1466     return num + this.getDate() - 1;
1467 };
1468
1469 /**
1470  * Get the string representation of the numeric week number of the year
1471  * (equivalent to the format specifier 'W').
1472  * @return {String} '00' through '52'
1473  */
1474 Date.prototype.getWeekOfYear = function() {
1475     // Skip to Thursday of this week
1476     var now = this.getDayOfYear() + (4 - this.getDay());
1477     // Find the first Thursday of the year
1478     var jan1 = new Date(this.getFullYear(), 0, 1);
1479     var then = (7 - jan1.getDay() + 4);
1480     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1481 };
1482
1483 /**
1484  * Whether or not the current date is in a leap year.
1485  * @return {Boolean} True if the current date is in a leap year, else false
1486  */
1487 Date.prototype.isLeapYear = function() {
1488     var year = this.getFullYear();
1489     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1490 };
1491
1492 /**
1493  * Get the first day of the current month, adjusted for leap year.  The returned value
1494  * is the numeric day index within the week (0-6) which can be used in conjunction with
1495  * the {@link #monthNames} array to retrieve the textual day name.
1496  * Example:
1497  *<pre><code>
1498 var dt = new Date('1/10/2007');
1499 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1500 </code></pre>
1501  * @return {Number} The day number (0-6)
1502  */
1503 Date.prototype.getFirstDayOfMonth = function() {
1504     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1505     return (day < 0) ? (day + 7) : day;
1506 };
1507
1508 /**
1509  * Get the last day of the current month, adjusted for leap year.  The returned value
1510  * is the numeric day index within the week (0-6) which can be used in conjunction with
1511  * the {@link #monthNames} array to retrieve the textual day name.
1512  * Example:
1513  *<pre><code>
1514 var dt = new Date('1/10/2007');
1515 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1516 </code></pre>
1517  * @return {Number} The day number (0-6)
1518  */
1519 Date.prototype.getLastDayOfMonth = function() {
1520     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1521     return (day < 0) ? (day + 7) : day;
1522 };
1523
1524
1525 /**
1526  * Get the first date of this date's month
1527  * @return {Date}
1528  */
1529 Date.prototype.getFirstDateOfMonth = function() {
1530     return new Date(this.getFullYear(), this.getMonth(), 1);
1531 };
1532
1533 /**
1534  * Get the last date of this date's month
1535  * @return {Date}
1536  */
1537 Date.prototype.getLastDateOfMonth = function() {
1538     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1539 };
1540 /**
1541  * Get the number of days in the current month, adjusted for leap year.
1542  * @return {Number} The number of days in the month
1543  */
1544 Date.prototype.getDaysInMonth = function() {
1545     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1546     return Date.daysInMonth[this.getMonth()];
1547 };
1548
1549 /**
1550  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1551  * @return {String} 'st, 'nd', 'rd' or 'th'
1552  */
1553 Date.prototype.getSuffix = function() {
1554     switch (this.getDate()) {
1555         case 1:
1556         case 21:
1557         case 31:
1558             return "st";
1559         case 2:
1560         case 22:
1561             return "nd";
1562         case 3:
1563         case 23:
1564             return "rd";
1565         default:
1566             return "th";
1567     }
1568 };
1569
1570 // private
1571 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1572
1573 /**
1574  * An array of textual month names.
1575  * Override these values for international dates, for example...
1576  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1577  * @type Array
1578  * @static
1579  */
1580 Date.monthNames =
1581    ["January",
1582     "February",
1583     "March",
1584     "April",
1585     "May",
1586     "June",
1587     "July",
1588     "August",
1589     "September",
1590     "October",
1591     "November",
1592     "December"];
1593
1594 /**
1595  * An array of textual day names.
1596  * Override these values for international dates, for example...
1597  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1598  * @type Array
1599  * @static
1600  */
1601 Date.dayNames =
1602    ["Sunday",
1603     "Monday",
1604     "Tuesday",
1605     "Wednesday",
1606     "Thursday",
1607     "Friday",
1608     "Saturday"];
1609
1610 // private
1611 Date.y2kYear = 50;
1612 // private
1613 Date.monthNumbers = {
1614     Jan:0,
1615     Feb:1,
1616     Mar:2,
1617     Apr:3,
1618     May:4,
1619     Jun:5,
1620     Jul:6,
1621     Aug:7,
1622     Sep:8,
1623     Oct:9,
1624     Nov:10,
1625     Dec:11};
1626
1627 /**
1628  * Creates and returns a new Date instance with the exact same date value as the called instance.
1629  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1630  * variable will also be changed.  When the intention is to create a new variable that will not
1631  * modify the original instance, you should create a clone.
1632  *
1633  * Example of correctly cloning a date:
1634  * <pre><code>
1635 //wrong way:
1636 var orig = new Date('10/1/2006');
1637 var copy = orig;
1638 copy.setDate(5);
1639 document.write(orig);  //returns 'Thu Oct 05 2006'!
1640
1641 //correct way:
1642 var orig = new Date('10/1/2006');
1643 var copy = orig.clone();
1644 copy.setDate(5);
1645 document.write(orig);  //returns 'Thu Oct 01 2006'
1646 </code></pre>
1647  * @return {Date} The new Date instance
1648  */
1649 Date.prototype.clone = function() {
1650         return new Date(this.getTime());
1651 };
1652
1653 /**
1654  * Clears any time information from this date
1655  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1656  @return {Date} this or the clone
1657  */
1658 Date.prototype.clearTime = function(clone){
1659     if(clone){
1660         return this.clone().clearTime();
1661     }
1662     this.setHours(0);
1663     this.setMinutes(0);
1664     this.setSeconds(0);
1665     this.setMilliseconds(0);
1666     return this;
1667 };
1668
1669 // private
1670 // safari setMonth is broken
1671 if(Roo.isSafari){
1672     Date.brokenSetMonth = Date.prototype.setMonth;
1673         Date.prototype.setMonth = function(num){
1674                 if(num <= -1){
1675                         var n = Math.ceil(-num);
1676                         var back_year = Math.ceil(n/12);
1677                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1678                         this.setFullYear(this.getFullYear() - back_year);
1679                         return Date.brokenSetMonth.call(this, month);
1680                 } else {
1681                         return Date.brokenSetMonth.apply(this, arguments);
1682                 }
1683         };
1684 }
1685
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.MILLI = "ms";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.SECOND = "s";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.MINUTE = "mi";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.HOUR = "h";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.DAY = "d";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.MONTH = "mo";
1710 /** Date interval constant 
1711 * @static 
1712 * @type String */
1713 Date.YEAR = "y";
1714
1715 /**
1716  * Provides a convenient method of performing basic date arithmetic.  This method
1717  * does not modify the Date instance being called - it creates and returns
1718  * a new Date instance containing the resulting date value.
1719  *
1720  * Examples:
1721  * <pre><code>
1722 //Basic usage:
1723 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1724 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1725
1726 //Negative values will subtract correctly:
1727 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1728 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1729
1730 //You can even chain several calls together in one line!
1731 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1732 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1733  </code></pre>
1734  *
1735  * @param {String} interval   A valid date interval enum value
1736  * @param {Number} value      The amount to add to the current date
1737  * @return {Date} The new Date instance
1738  */
1739 Date.prototype.add = function(interval, value){
1740   var d = this.clone();
1741   if (!interval || value === 0) return d;
1742   switch(interval.toLowerCase()){
1743     case Date.MILLI:
1744       d.setMilliseconds(this.getMilliseconds() + value);
1745       break;
1746     case Date.SECOND:
1747       d.setSeconds(this.getSeconds() + value);
1748       break;
1749     case Date.MINUTE:
1750       d.setMinutes(this.getMinutes() + value);
1751       break;
1752     case Date.HOUR:
1753       d.setHours(this.getHours() + value);
1754       break;
1755     case Date.DAY:
1756       d.setDate(this.getDate() + value);
1757       break;
1758     case Date.MONTH:
1759       var day = this.getDate();
1760       if(day > 28){
1761           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1762       }
1763       d.setDate(day);
1764       d.setMonth(this.getMonth() + value);
1765       break;
1766     case Date.YEAR:
1767       d.setFullYear(this.getFullYear() + value);
1768       break;
1769   }
1770   return d;
1771 };
1772 /*
1773  * Based on:
1774  * Ext JS Library 1.1.1
1775  * Copyright(c) 2006-2007, Ext JS, LLC.
1776  *
1777  * Originally Released Under LGPL - original licence link has changed is not relivant.
1778  *
1779  * Fork - LGPL
1780  * <script type="text/javascript">
1781  */
1782
1783 /**
1784  * @class Roo.lib.Dom
1785  * @static
1786  * 
1787  * Dom utils (from YIU afaik)
1788  * 
1789  **/
1790 Roo.lib.Dom = {
1791     /**
1792      * Get the view width
1793      * @param {Boolean} full True will get the full document, otherwise it's the view width
1794      * @return {Number} The width
1795      */
1796      
1797     getViewWidth : function(full) {
1798         return full ? this.getDocumentWidth() : this.getViewportWidth();
1799     },
1800     /**
1801      * Get the view height
1802      * @param {Boolean} full True will get the full document, otherwise it's the view height
1803      * @return {Number} The height
1804      */
1805     getViewHeight : function(full) {
1806         return full ? this.getDocumentHeight() : this.getViewportHeight();
1807     },
1808
1809     getDocumentHeight: function() {
1810         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1811         return Math.max(scrollHeight, this.getViewportHeight());
1812     },
1813
1814     getDocumentWidth: function() {
1815         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1816         return Math.max(scrollWidth, this.getViewportWidth());
1817     },
1818
1819     getViewportHeight: function() {
1820         var height = self.innerHeight;
1821         var mode = document.compatMode;
1822
1823         if ((mode || Roo.isIE) && !Roo.isOpera) {
1824             height = (mode == "CSS1Compat") ?
1825                      document.documentElement.clientHeight :
1826                      document.body.clientHeight;
1827         }
1828
1829         return height;
1830     },
1831
1832     getViewportWidth: function() {
1833         var width = self.innerWidth;
1834         var mode = document.compatMode;
1835
1836         if (mode || Roo.isIE) {
1837             width = (mode == "CSS1Compat") ?
1838                     document.documentElement.clientWidth :
1839                     document.body.clientWidth;
1840         }
1841         return width;
1842     },
1843
1844     isAncestor : function(p, c) {
1845         p = Roo.getDom(p);
1846         c = Roo.getDom(c);
1847         if (!p || !c) {
1848             return false;
1849         }
1850
1851         if (p.contains && !Roo.isSafari) {
1852             return p.contains(c);
1853         } else if (p.compareDocumentPosition) {
1854             return !!(p.compareDocumentPosition(c) & 16);
1855         } else {
1856             var parent = c.parentNode;
1857             while (parent) {
1858                 if (parent == p) {
1859                     return true;
1860                 }
1861                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1862                     return false;
1863                 }
1864                 parent = parent.parentNode;
1865             }
1866             return false;
1867         }
1868     },
1869
1870     getRegion : function(el) {
1871         return Roo.lib.Region.getRegion(el);
1872     },
1873
1874     getY : function(el) {
1875         return this.getXY(el)[1];
1876     },
1877
1878     getX : function(el) {
1879         return this.getXY(el)[0];
1880     },
1881
1882     getXY : function(el) {
1883         var p, pe, b, scroll, bd = document.body;
1884         el = Roo.getDom(el);
1885         var fly = Roo.lib.AnimBase.fly;
1886         if (el.getBoundingClientRect) {
1887             b = el.getBoundingClientRect();
1888             scroll = fly(document).getScroll();
1889             return [b.left + scroll.left, b.top + scroll.top];
1890         }
1891         var x = 0, y = 0;
1892
1893         p = el;
1894
1895         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1896
1897         while (p) {
1898
1899             x += p.offsetLeft;
1900             y += p.offsetTop;
1901
1902             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1903                 hasAbsolute = true;
1904             }
1905
1906             if (Roo.isGecko) {
1907                 pe = fly(p);
1908
1909                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1910                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1911
1912
1913                 x += bl;
1914                 y += bt;
1915
1916
1917                 if (p != el && pe.getStyle('overflow') != 'visible') {
1918                     x += bl;
1919                     y += bt;
1920                 }
1921             }
1922             p = p.offsetParent;
1923         }
1924
1925         if (Roo.isSafari && hasAbsolute) {
1926             x -= bd.offsetLeft;
1927             y -= bd.offsetTop;
1928         }
1929
1930         if (Roo.isGecko && !hasAbsolute) {
1931             var dbd = fly(bd);
1932             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1933             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1934         }
1935
1936         p = el.parentNode;
1937         while (p && p != bd) {
1938             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1939                 x -= p.scrollLeft;
1940                 y -= p.scrollTop;
1941             }
1942             p = p.parentNode;
1943         }
1944         return [x, y];
1945     },
1946  
1947   
1948
1949
1950     setXY : function(el, xy) {
1951         el = Roo.fly(el, '_setXY');
1952         el.position();
1953         var pts = el.translatePoints(xy);
1954         if (xy[0] !== false) {
1955             el.dom.style.left = pts.left + "px";
1956         }
1957         if (xy[1] !== false) {
1958             el.dom.style.top = pts.top + "px";
1959         }
1960     },
1961
1962     setX : function(el, x) {
1963         this.setXY(el, [x, false]);
1964     },
1965
1966     setY : function(el, y) {
1967         this.setXY(el, [false, y]);
1968     }
1969 };
1970 /*
1971  * Portions of this file are based on pieces of Yahoo User Interface Library
1972  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1973  * YUI licensed under the BSD License:
1974  * http://developer.yahoo.net/yui/license.txt
1975  * <script type="text/javascript">
1976  *
1977  */
1978
1979 Roo.lib.Event = function() {
1980     var loadComplete = false;
1981     var listeners = [];
1982     var unloadListeners = [];
1983     var retryCount = 0;
1984     var onAvailStack = [];
1985     var counter = 0;
1986     var lastError = null;
1987
1988     return {
1989         POLL_RETRYS: 200,
1990         POLL_INTERVAL: 20,
1991         EL: 0,
1992         TYPE: 1,
1993         FN: 2,
1994         WFN: 3,
1995         OBJ: 3,
1996         ADJ_SCOPE: 4,
1997         _interval: null,
1998
1999         startInterval: function() {
2000             if (!this._interval) {
2001                 var self = this;
2002                 var callback = function() {
2003                     self._tryPreloadAttach();
2004                 };
2005                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2006
2007             }
2008         },
2009
2010         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2011             onAvailStack.push({ id:         p_id,
2012                 fn:         p_fn,
2013                 obj:        p_obj,
2014                 override:   p_override,
2015                 checkReady: false    });
2016
2017             retryCount = this.POLL_RETRYS;
2018             this.startInterval();
2019         },
2020
2021
2022         addListener: function(el, eventName, fn) {
2023             el = Roo.getDom(el);
2024             if (!el || !fn) {
2025                 return false;
2026             }
2027
2028             if ("unload" == eventName) {
2029                 unloadListeners[unloadListeners.length] =
2030                 [el, eventName, fn];
2031                 return true;
2032             }
2033
2034             var wrappedFn = function(e) {
2035                 return fn(Roo.lib.Event.getEvent(e));
2036             };
2037
2038             var li = [el, eventName, fn, wrappedFn];
2039
2040             var index = listeners.length;
2041             listeners[index] = li;
2042
2043             this.doAdd(el, eventName, wrappedFn, false);
2044             return true;
2045
2046         },
2047
2048
2049         removeListener: function(el, eventName, fn) {
2050             var i, len;
2051
2052             el = Roo.getDom(el);
2053
2054             if(!fn) {
2055                 return this.purgeElement(el, false, eventName);
2056             }
2057
2058
2059             if ("unload" == eventName) {
2060
2061                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2062                     var li = unloadListeners[i];
2063                     if (li &&
2064                         li[0] == el &&
2065                         li[1] == eventName &&
2066                         li[2] == fn) {
2067                         unloadListeners.splice(i, 1);
2068                         return true;
2069                     }
2070                 }
2071
2072                 return false;
2073             }
2074
2075             var cacheItem = null;
2076
2077
2078             var index = arguments[3];
2079
2080             if ("undefined" == typeof index) {
2081                 index = this._getCacheIndex(el, eventName, fn);
2082             }
2083
2084             if (index >= 0) {
2085                 cacheItem = listeners[index];
2086             }
2087
2088             if (!el || !cacheItem) {
2089                 return false;
2090             }
2091
2092             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2093
2094             delete listeners[index][this.WFN];
2095             delete listeners[index][this.FN];
2096             listeners.splice(index, 1);
2097
2098             return true;
2099
2100         },
2101
2102
2103         getTarget: function(ev, resolveTextNode) {
2104             ev = ev.browserEvent || ev;
2105             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2106             var t = ev.target || ev.srcElement;
2107             return this.resolveTextNode(t);
2108         },
2109
2110
2111         resolveTextNode: function(node) {
2112             if (Roo.isSafari && node && 3 == node.nodeType) {
2113                 return node.parentNode;
2114             } else {
2115                 return node;
2116             }
2117         },
2118
2119
2120         getPageX: function(ev) {
2121             ev = ev.browserEvent || ev;
2122             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2123             var x = ev.pageX;
2124             if (!x && 0 !== x) {
2125                 x = ev.clientX || 0;
2126
2127                 if (Roo.isIE) {
2128                     x += this.getScroll()[1];
2129                 }
2130             }
2131
2132             return x;
2133         },
2134
2135
2136         getPageY: function(ev) {
2137             ev = ev.browserEvent || ev;
2138             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2139             var y = ev.pageY;
2140             if (!y && 0 !== y) {
2141                 y = ev.clientY || 0;
2142
2143                 if (Roo.isIE) {
2144                     y += this.getScroll()[0];
2145                 }
2146             }
2147
2148
2149             return y;
2150         },
2151
2152
2153         getXY: function(ev) {
2154             ev = ev.browserEvent || ev;
2155             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2156             return [this.getPageX(ev), this.getPageY(ev)];
2157         },
2158
2159
2160         getRelatedTarget: function(ev) {
2161             ev = ev.browserEvent || ev;
2162             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2163             var t = ev.relatedTarget;
2164             if (!t) {
2165                 if (ev.type == "mouseout") {
2166                     t = ev.toElement;
2167                 } else if (ev.type == "mouseover") {
2168                     t = ev.fromElement;
2169                 }
2170             }
2171
2172             return this.resolveTextNode(t);
2173         },
2174
2175
2176         getTime: function(ev) {
2177             ev = ev.browserEvent || ev;
2178             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2179             if (!ev.time) {
2180                 var t = new Date().getTime();
2181                 try {
2182                     ev.time = t;
2183                 } catch(ex) {
2184                     this.lastError = ex;
2185                     return t;
2186                 }
2187             }
2188
2189             return ev.time;
2190         },
2191
2192
2193         stopEvent: function(ev) {
2194             this.stopPropagation(ev);
2195             this.preventDefault(ev);
2196         },
2197
2198
2199         stopPropagation: function(ev) {
2200             ev = ev.browserEvent || ev;
2201             if (ev.stopPropagation) {
2202                 ev.stopPropagation();
2203             } else {
2204                 ev.cancelBubble = true;
2205             }
2206         },
2207
2208
2209         preventDefault: function(ev) {
2210             ev = ev.browserEvent || ev;
2211             if(ev.preventDefault) {
2212                 ev.preventDefault();
2213             } else {
2214                 ev.returnValue = false;
2215             }
2216         },
2217
2218
2219         getEvent: function(e) {
2220             var ev = e || window.event;
2221             if (!ev) {
2222                 var c = this.getEvent.caller;
2223                 while (c) {
2224                     ev = c.arguments[0];
2225                     if (ev && Event == ev.constructor) {
2226                         break;
2227                     }
2228                     c = c.caller;
2229                 }
2230             }
2231             return ev;
2232         },
2233
2234
2235         getCharCode: function(ev) {
2236             ev = ev.browserEvent || ev;
2237             return ev.charCode || ev.keyCode || 0;
2238         },
2239
2240
2241         _getCacheIndex: function(el, eventName, fn) {
2242             for (var i = 0,len = listeners.length; i < len; ++i) {
2243                 var li = listeners[i];
2244                 if (li &&
2245                     li[this.FN] == fn &&
2246                     li[this.EL] == el &&
2247                     li[this.TYPE] == eventName) {
2248                     return i;
2249                 }
2250             }
2251
2252             return -1;
2253         },
2254
2255
2256         elCache: {},
2257
2258
2259         getEl: function(id) {
2260             return document.getElementById(id);
2261         },
2262
2263
2264         clearCache: function() {
2265         },
2266
2267
2268         _load: function(e) {
2269             loadComplete = true;
2270             var EU = Roo.lib.Event;
2271
2272
2273             if (Roo.isIE) {
2274                 EU.doRemove(window, "load", EU._load);
2275             }
2276         },
2277
2278
2279         _tryPreloadAttach: function() {
2280
2281             if (this.locked) {
2282                 return false;
2283             }
2284
2285             this.locked = true;
2286
2287
2288             var tryAgain = !loadComplete;
2289             if (!tryAgain) {
2290                 tryAgain = (retryCount > 0);
2291             }
2292
2293
2294             var notAvail = [];
2295             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2296                 var item = onAvailStack[i];
2297                 if (item) {
2298                     var el = this.getEl(item.id);
2299
2300                     if (el) {
2301                         if (!item.checkReady ||
2302                             loadComplete ||
2303                             el.nextSibling ||
2304                             (document && document.body)) {
2305
2306                             var scope = el;
2307                             if (item.override) {
2308                                 if (item.override === true) {
2309                                     scope = item.obj;
2310                                 } else {
2311                                     scope = item.override;
2312                                 }
2313                             }
2314                             item.fn.call(scope, item.obj);
2315                             onAvailStack[i] = null;
2316                         }
2317                     } else {
2318                         notAvail.push(item);
2319                     }
2320                 }
2321             }
2322
2323             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2324
2325             if (tryAgain) {
2326
2327                 this.startInterval();
2328             } else {
2329                 clearInterval(this._interval);
2330                 this._interval = null;
2331             }
2332
2333             this.locked = false;
2334
2335             return true;
2336
2337         },
2338
2339
2340         purgeElement: function(el, recurse, eventName) {
2341             var elListeners = this.getListeners(el, eventName);
2342             if (elListeners) {
2343                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2344                     var l = elListeners[i];
2345                     this.removeListener(el, l.type, l.fn);
2346                 }
2347             }
2348
2349             if (recurse && el && el.childNodes) {
2350                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2351                     this.purgeElement(el.childNodes[i], recurse, eventName);
2352                 }
2353             }
2354         },
2355
2356
2357         getListeners: function(el, eventName) {
2358             var results = [], searchLists;
2359             if (!eventName) {
2360                 searchLists = [listeners, unloadListeners];
2361             } else if (eventName == "unload") {
2362                 searchLists = [unloadListeners];
2363             } else {
2364                 searchLists = [listeners];
2365             }
2366
2367             for (var j = 0; j < searchLists.length; ++j) {
2368                 var searchList = searchLists[j];
2369                 if (searchList && searchList.length > 0) {
2370                     for (var i = 0,len = searchList.length; i < len; ++i) {
2371                         var l = searchList[i];
2372                         if (l && l[this.EL] === el &&
2373                             (!eventName || eventName === l[this.TYPE])) {
2374                             results.push({
2375                                 type:   l[this.TYPE],
2376                                 fn:     l[this.FN],
2377                                 obj:    l[this.OBJ],
2378                                 adjust: l[this.ADJ_SCOPE],
2379                                 index:  i
2380                             });
2381                         }
2382                     }
2383                 }
2384             }
2385
2386             return (results.length) ? results : null;
2387         },
2388
2389
2390         _unload: function(e) {
2391
2392             var EU = Roo.lib.Event, i, j, l, len, index;
2393
2394             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2395                 l = unloadListeners[i];
2396                 if (l) {
2397                     var scope = window;
2398                     if (l[EU.ADJ_SCOPE]) {
2399                         if (l[EU.ADJ_SCOPE] === true) {
2400                             scope = l[EU.OBJ];
2401                         } else {
2402                             scope = l[EU.ADJ_SCOPE];
2403                         }
2404                     }
2405                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2406                     unloadListeners[i] = null;
2407                     l = null;
2408                     scope = null;
2409                 }
2410             }
2411
2412             unloadListeners = null;
2413
2414             if (listeners && listeners.length > 0) {
2415                 j = listeners.length;
2416                 while (j) {
2417                     index = j - 1;
2418                     l = listeners[index];
2419                     if (l) {
2420                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2421                                 l[EU.FN], index);
2422                     }
2423                     j = j - 1;
2424                 }
2425                 l = null;
2426
2427                 EU.clearCache();
2428             }
2429
2430             EU.doRemove(window, "unload", EU._unload);
2431
2432         },
2433
2434
2435         getScroll: function() {
2436             var dd = document.documentElement, db = document.body;
2437             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2438                 return [dd.scrollTop, dd.scrollLeft];
2439             } else if (db) {
2440                 return [db.scrollTop, db.scrollLeft];
2441             } else {
2442                 return [0, 0];
2443             }
2444         },
2445
2446
2447         doAdd: function () {
2448             if (window.addEventListener) {
2449                 return function(el, eventName, fn, capture) {
2450                     el.addEventListener(eventName, fn, (capture));
2451                 };
2452             } else if (window.attachEvent) {
2453                 return function(el, eventName, fn, capture) {
2454                     el.attachEvent("on" + eventName, fn);
2455                 };
2456             } else {
2457                 return function() {
2458                 };
2459             }
2460         }(),
2461
2462
2463         doRemove: function() {
2464             if (window.removeEventListener) {
2465                 return function (el, eventName, fn, capture) {
2466                     el.removeEventListener(eventName, fn, (capture));
2467                 };
2468             } else if (window.detachEvent) {
2469                 return function (el, eventName, fn) {
2470                     el.detachEvent("on" + eventName, fn);
2471                 };
2472             } else {
2473                 return function() {
2474                 };
2475             }
2476         }()
2477     };
2478     
2479 }();
2480 (function() {     
2481    
2482     var E = Roo.lib.Event;
2483     E.on = E.addListener;
2484     E.un = E.removeListener;
2485
2486     if (document && document.body) {
2487         E._load();
2488     } else {
2489         E.doAdd(window, "load", E._load);
2490     }
2491     E.doAdd(window, "unload", E._unload);
2492     E._tryPreloadAttach();
2493 })();
2494
2495 /*
2496  * Portions of this file are based on pieces of Yahoo User Interface Library
2497  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2498  * YUI licensed under the BSD License:
2499  * http://developer.yahoo.net/yui/license.txt
2500  * <script type="text/javascript">
2501  *
2502  */
2503
2504 (function() {
2505     /**
2506      * @class Roo.lib.Ajax
2507      *
2508      */
2509     Roo.lib.Ajax = {
2510         /**
2511          * @static 
2512          */
2513         request : function(method, uri, cb, data, options) {
2514             if(options){
2515                 var hs = options.headers;
2516                 if(hs){
2517                     for(var h in hs){
2518                         if(hs.hasOwnProperty(h)){
2519                             this.initHeader(h, hs[h], false);
2520                         }
2521                     }
2522                 }
2523                 if(options.xmlData){
2524                     this.initHeader('Content-Type', 'text/xml', false);
2525                     method = 'POST';
2526                     data = options.xmlData;
2527                 }
2528             }
2529
2530             return this.asyncRequest(method, uri, cb, data);
2531         },
2532
2533         serializeForm : function(form) {
2534             if(typeof form == 'string') {
2535                 form = (document.getElementById(form) || document.forms[form]);
2536             }
2537
2538             var el, name, val, disabled, data = '', hasSubmit = false;
2539             for (var i = 0; i < form.elements.length; i++) {
2540                 el = form.elements[i];
2541                 disabled = form.elements[i].disabled;
2542                 name = form.elements[i].name;
2543                 val = form.elements[i].value;
2544
2545                 if (!disabled && name){
2546                     switch (el.type)
2547                             {
2548                         case 'select-one':
2549                         case 'select-multiple':
2550                             for (var j = 0; j < el.options.length; j++) {
2551                                 if (el.options[j].selected) {
2552                                     if (Roo.isIE) {
2553                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2554                                     }
2555                                     else {
2556                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2557                                     }
2558                                 }
2559                             }
2560                             break;
2561                         case 'radio':
2562                         case 'checkbox':
2563                             if (el.checked) {
2564                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2565                             }
2566                             break;
2567                         case 'file':
2568
2569                         case undefined:
2570
2571                         case 'reset':
2572
2573                         case 'button':
2574
2575                             break;
2576                         case 'submit':
2577                             if(hasSubmit == false) {
2578                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2579                                 hasSubmit = true;
2580                             }
2581                             break;
2582                         default:
2583                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2584                             break;
2585                     }
2586                 }
2587             }
2588             data = data.substr(0, data.length - 1);
2589             return data;
2590         },
2591
2592         headers:{},
2593
2594         hasHeaders:false,
2595
2596         useDefaultHeader:true,
2597
2598         defaultPostHeader:'application/x-www-form-urlencoded',
2599
2600         useDefaultXhrHeader:true,
2601
2602         defaultXhrHeader:'XMLHttpRequest',
2603
2604         hasDefaultHeaders:true,
2605
2606         defaultHeaders:{},
2607
2608         poll:{},
2609
2610         timeout:{},
2611
2612         pollInterval:50,
2613
2614         transactionId:0,
2615
2616         setProgId:function(id)
2617         {
2618             this.activeX.unshift(id);
2619         },
2620
2621         setDefaultPostHeader:function(b)
2622         {
2623             this.useDefaultHeader = b;
2624         },
2625
2626         setDefaultXhrHeader:function(b)
2627         {
2628             this.useDefaultXhrHeader = b;
2629         },
2630
2631         setPollingInterval:function(i)
2632         {
2633             if (typeof i == 'number' && isFinite(i)) {
2634                 this.pollInterval = i;
2635             }
2636         },
2637
2638         createXhrObject:function(transactionId)
2639         {
2640             var obj,http;
2641             try
2642             {
2643
2644                 http = new XMLHttpRequest();
2645
2646                 obj = { conn:http, tId:transactionId };
2647             }
2648             catch(e)
2649             {
2650                 for (var i = 0; i < this.activeX.length; ++i) {
2651                     try
2652                     {
2653
2654                         http = new ActiveXObject(this.activeX[i]);
2655
2656                         obj = { conn:http, tId:transactionId };
2657                         break;
2658                     }
2659                     catch(e) {
2660                     }
2661                 }
2662             }
2663             finally
2664             {
2665                 return obj;
2666             }
2667         },
2668
2669         getConnectionObject:function()
2670         {
2671             var o;
2672             var tId = this.transactionId;
2673
2674             try
2675             {
2676                 o = this.createXhrObject(tId);
2677                 if (o) {
2678                     this.transactionId++;
2679                 }
2680             }
2681             catch(e) {
2682             }
2683             finally
2684             {
2685                 return o;
2686             }
2687         },
2688
2689         asyncRequest:function(method, uri, callback, postData)
2690         {
2691             var o = this.getConnectionObject();
2692
2693             if (!o) {
2694                 return null;
2695             }
2696             else {
2697                 o.conn.open(method, uri, true);
2698
2699                 if (this.useDefaultXhrHeader) {
2700                     if (!this.defaultHeaders['X-Requested-With']) {
2701                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2702                     }
2703                 }
2704
2705                 if(postData && this.useDefaultHeader){
2706                     this.initHeader('Content-Type', this.defaultPostHeader);
2707                 }
2708
2709                  if (this.hasDefaultHeaders || this.hasHeaders) {
2710                     this.setHeader(o);
2711                 }
2712
2713                 this.handleReadyState(o, callback);
2714                 o.conn.send(postData || null);
2715
2716                 return o;
2717             }
2718         },
2719
2720         handleReadyState:function(o, callback)
2721         {
2722             var oConn = this;
2723
2724             if (callback && callback.timeout) {
2725                 
2726                 this.timeout[o.tId] = window.setTimeout(function() {
2727                     oConn.abort(o, callback, true);
2728                 }, callback.timeout);
2729             }
2730
2731             this.poll[o.tId] = window.setInterval(
2732                     function() {
2733                         if (o.conn && o.conn.readyState == 4) {
2734                             window.clearInterval(oConn.poll[o.tId]);
2735                             delete oConn.poll[o.tId];
2736
2737                             if(callback && callback.timeout) {
2738                                 window.clearTimeout(oConn.timeout[o.tId]);
2739                                 delete oConn.timeout[o.tId];
2740                             }
2741
2742                             oConn.handleTransactionResponse(o, callback);
2743                         }
2744                     }
2745                     , this.pollInterval);
2746         },
2747
2748         handleTransactionResponse:function(o, callback, isAbort)
2749         {
2750
2751             if (!callback) {
2752                 this.releaseObject(o);
2753                 return;
2754             }
2755
2756             var httpStatus, responseObject;
2757
2758             try
2759             {
2760                 if (o.conn.status !== undefined && o.conn.status != 0) {
2761                     httpStatus = o.conn.status;
2762                 }
2763                 else {
2764                     httpStatus = 13030;
2765                 }
2766             }
2767             catch(e) {
2768
2769
2770                 httpStatus = 13030;
2771             }
2772
2773             if (httpStatus >= 200 && httpStatus < 300) {
2774                 responseObject = this.createResponseObject(o, callback.argument);
2775                 if (callback.success) {
2776                     if (!callback.scope) {
2777                         callback.success(responseObject);
2778                     }
2779                     else {
2780
2781
2782                         callback.success.apply(callback.scope, [responseObject]);
2783                     }
2784                 }
2785             }
2786             else {
2787                 switch (httpStatus) {
2788
2789                     case 12002:
2790                     case 12029:
2791                     case 12030:
2792                     case 12031:
2793                     case 12152:
2794                     case 13030:
2795                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2796                         if (callback.failure) {
2797                             if (!callback.scope) {
2798                                 callback.failure(responseObject);
2799                             }
2800                             else {
2801                                 callback.failure.apply(callback.scope, [responseObject]);
2802                             }
2803                         }
2804                         break;
2805                     default:
2806                         responseObject = this.createResponseObject(o, callback.argument);
2807                         if (callback.failure) {
2808                             if (!callback.scope) {
2809                                 callback.failure(responseObject);
2810                             }
2811                             else {
2812                                 callback.failure.apply(callback.scope, [responseObject]);
2813                             }
2814                         }
2815                 }
2816             }
2817
2818             this.releaseObject(o);
2819             responseObject = null;
2820         },
2821
2822         createResponseObject:function(o, callbackArg)
2823         {
2824             var obj = {};
2825             var headerObj = {};
2826
2827             try
2828             {
2829                 var headerStr = o.conn.getAllResponseHeaders();
2830                 var header = headerStr.split('\n');
2831                 for (var i = 0; i < header.length; i++) {
2832                     var delimitPos = header[i].indexOf(':');
2833                     if (delimitPos != -1) {
2834                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2835                     }
2836                 }
2837             }
2838             catch(e) {
2839             }
2840
2841             obj.tId = o.tId;
2842             obj.status = o.conn.status;
2843             obj.statusText = o.conn.statusText;
2844             obj.getResponseHeader = headerObj;
2845             obj.getAllResponseHeaders = headerStr;
2846             obj.responseText = o.conn.responseText;
2847             obj.responseXML = o.conn.responseXML;
2848
2849             if (typeof callbackArg !== undefined) {
2850                 obj.argument = callbackArg;
2851             }
2852
2853             return obj;
2854         },
2855
2856         createExceptionObject:function(tId, callbackArg, isAbort)
2857         {
2858             var COMM_CODE = 0;
2859             var COMM_ERROR = 'communication failure';
2860             var ABORT_CODE = -1;
2861             var ABORT_ERROR = 'transaction aborted';
2862
2863             var obj = {};
2864
2865             obj.tId = tId;
2866             if (isAbort) {
2867                 obj.status = ABORT_CODE;
2868                 obj.statusText = ABORT_ERROR;
2869             }
2870             else {
2871                 obj.status = COMM_CODE;
2872                 obj.statusText = COMM_ERROR;
2873             }
2874
2875             if (callbackArg) {
2876                 obj.argument = callbackArg;
2877             }
2878
2879             return obj;
2880         },
2881
2882         initHeader:function(label, value, isDefault)
2883         {
2884             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2885
2886             if (headerObj[label] === undefined) {
2887                 headerObj[label] = value;
2888             }
2889             else {
2890
2891
2892                 headerObj[label] = value + "," + headerObj[label];
2893             }
2894
2895             if (isDefault) {
2896                 this.hasDefaultHeaders = true;
2897             }
2898             else {
2899                 this.hasHeaders = true;
2900             }
2901         },
2902
2903
2904         setHeader:function(o)
2905         {
2906             if (this.hasDefaultHeaders) {
2907                 for (var prop in this.defaultHeaders) {
2908                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2909                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2910                     }
2911                 }
2912             }
2913
2914             if (this.hasHeaders) {
2915                 for (var prop in this.headers) {
2916                     if (this.headers.hasOwnProperty(prop)) {
2917                         o.conn.setRequestHeader(prop, this.headers[prop]);
2918                     }
2919                 }
2920                 this.headers = {};
2921                 this.hasHeaders = false;
2922             }
2923         },
2924
2925         resetDefaultHeaders:function() {
2926             delete this.defaultHeaders;
2927             this.defaultHeaders = {};
2928             this.hasDefaultHeaders = false;
2929         },
2930
2931         abort:function(o, callback, isTimeout)
2932         {
2933             if(this.isCallInProgress(o)) {
2934                 o.conn.abort();
2935                 window.clearInterval(this.poll[o.tId]);
2936                 delete this.poll[o.tId];
2937                 if (isTimeout) {
2938                     delete this.timeout[o.tId];
2939                 }
2940
2941                 this.handleTransactionResponse(o, callback, true);
2942
2943                 return true;
2944             }
2945             else {
2946                 return false;
2947             }
2948         },
2949
2950
2951         isCallInProgress:function(o)
2952         {
2953             if (o && o.conn) {
2954                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2955             }
2956             else {
2957
2958                 return false;
2959             }
2960         },
2961
2962
2963         releaseObject:function(o)
2964         {
2965
2966             o.conn = null;
2967
2968             o = null;
2969         },
2970
2971         activeX:[
2972         'MSXML2.XMLHTTP.3.0',
2973         'MSXML2.XMLHTTP',
2974         'Microsoft.XMLHTTP'
2975         ]
2976
2977
2978     };
2979 })();/*
2980  * Portions of this file are based on pieces of Yahoo User Interface Library
2981  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2982  * YUI licensed under the BSD License:
2983  * http://developer.yahoo.net/yui/license.txt
2984  * <script type="text/javascript">
2985  *
2986  */
2987
2988 Roo.lib.Region = function(t, r, b, l) {
2989     this.top = t;
2990     this[1] = t;
2991     this.right = r;
2992     this.bottom = b;
2993     this.left = l;
2994     this[0] = l;
2995 };
2996
2997
2998 Roo.lib.Region.prototype = {
2999     contains : function(region) {
3000         return ( region.left >= this.left &&
3001                  region.right <= this.right &&
3002                  region.top >= this.top &&
3003                  region.bottom <= this.bottom    );
3004
3005     },
3006
3007     getArea : function() {
3008         return ( (this.bottom - this.top) * (this.right - this.left) );
3009     },
3010
3011     intersect : function(region) {
3012         var t = Math.max(this.top, region.top);
3013         var r = Math.min(this.right, region.right);
3014         var b = Math.min(this.bottom, region.bottom);
3015         var l = Math.max(this.left, region.left);
3016
3017         if (b >= t && r >= l) {
3018             return new Roo.lib.Region(t, r, b, l);
3019         } else {
3020             return null;
3021         }
3022     },
3023     union : function(region) {
3024         var t = Math.min(this.top, region.top);
3025         var r = Math.max(this.right, region.right);
3026         var b = Math.max(this.bottom, region.bottom);
3027         var l = Math.min(this.left, region.left);
3028
3029         return new Roo.lib.Region(t, r, b, l);
3030     },
3031
3032     adjust : function(t, l, b, r) {
3033         this.top += t;
3034         this.left += l;
3035         this.right += r;
3036         this.bottom += b;
3037         return this;
3038     }
3039 };
3040
3041 Roo.lib.Region.getRegion = function(el) {
3042     var p = Roo.lib.Dom.getXY(el);
3043
3044     var t = p[1];
3045     var r = p[0] + el.offsetWidth;
3046     var b = p[1] + el.offsetHeight;
3047     var l = p[0];
3048
3049     return new Roo.lib.Region(t, r, b, l);
3050 };
3051 /*
3052  * Portions of this file are based on pieces of Yahoo User Interface Library
3053  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3054  * YUI licensed under the BSD License:
3055  * http://developer.yahoo.net/yui/license.txt
3056  * <script type="text/javascript">
3057  *
3058  */
3059 //@@dep Roo.lib.Region
3060
3061
3062 Roo.lib.Point = function(x, y) {
3063     if (x instanceof Array) {
3064         y = x[1];
3065         x = x[0];
3066     }
3067     this.x = this.right = this.left = this[0] = x;
3068     this.y = this.top = this.bottom = this[1] = y;
3069 };
3070
3071 Roo.lib.Point.prototype = new Roo.lib.Region();
3072 /*
3073  * Portions of this file are based on pieces of Yahoo User Interface Library
3074  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3075  * YUI licensed under the BSD License:
3076  * http://developer.yahoo.net/yui/license.txt
3077  * <script type="text/javascript">
3078  *
3079  */
3080  
3081 (function() {   
3082
3083     Roo.lib.Anim = {
3084         scroll : function(el, args, duration, easing, cb, scope) {
3085             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3086         },
3087
3088         motion : function(el, args, duration, easing, cb, scope) {
3089             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3090         },
3091
3092         color : function(el, args, duration, easing, cb, scope) {
3093             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3094         },
3095
3096         run : function(el, args, duration, easing, cb, scope, type) {
3097             type = type || Roo.lib.AnimBase;
3098             if (typeof easing == "string") {
3099                 easing = Roo.lib.Easing[easing];
3100             }
3101             var anim = new type(el, args, duration, easing);
3102             anim.animateX(function() {
3103                 Roo.callback(cb, scope);
3104             });
3105             return anim;
3106         }
3107     };
3108 })();/*
3109  * Portions of this file are based on pieces of Yahoo User Interface Library
3110  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3111  * YUI licensed under the BSD License:
3112  * http://developer.yahoo.net/yui/license.txt
3113  * <script type="text/javascript">
3114  *
3115  */
3116
3117 (function() {    
3118     var libFlyweight;
3119     
3120     function fly(el) {
3121         if (!libFlyweight) {
3122             libFlyweight = new Roo.Element.Flyweight();
3123         }
3124         libFlyweight.dom = el;
3125         return libFlyweight;
3126     }
3127
3128     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3129     
3130    
3131     
3132     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3133         if (el) {
3134             this.init(el, attributes, duration, method);
3135         }
3136     };
3137
3138     Roo.lib.AnimBase.fly = fly;
3139     
3140     
3141     
3142     Roo.lib.AnimBase.prototype = {
3143
3144         toString: function() {
3145             var el = this.getEl();
3146             var id = el.id || el.tagName;
3147             return ("Anim " + id);
3148         },
3149
3150         patterns: {
3151             noNegatives:        /width|height|opacity|padding/i,
3152             offsetAttribute:  /^((width|height)|(top|left))$/,
3153             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3154             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3155         },
3156
3157
3158         doMethod: function(attr, start, end) {
3159             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3160         },
3161
3162
3163         setAttribute: function(attr, val, unit) {
3164             if (this.patterns.noNegatives.test(attr)) {
3165                 val = (val > 0) ? val : 0;
3166             }
3167
3168             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3169         },
3170
3171
3172         getAttribute: function(attr) {
3173             var el = this.getEl();
3174             var val = fly(el).getStyle(attr);
3175
3176             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3177                 return parseFloat(val);
3178             }
3179
3180             var a = this.patterns.offsetAttribute.exec(attr) || [];
3181             var pos = !!( a[3] );
3182             var box = !!( a[2] );
3183
3184
3185             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3186                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3187             } else {
3188                 val = 0;
3189             }
3190
3191             return val;
3192         },
3193
3194
3195         getDefaultUnit: function(attr) {
3196             if (this.patterns.defaultUnit.test(attr)) {
3197                 return 'px';
3198             }
3199
3200             return '';
3201         },
3202
3203         animateX : function(callback, scope) {
3204             var f = function() {
3205                 this.onComplete.removeListener(f);
3206                 if (typeof callback == "function") {
3207                     callback.call(scope || this, this);
3208                 }
3209             };
3210             this.onComplete.addListener(f, this);
3211             this.animate();
3212         },
3213
3214
3215         setRuntimeAttribute: function(attr) {
3216             var start;
3217             var end;
3218             var attributes = this.attributes;
3219
3220             this.runtimeAttributes[attr] = {};
3221
3222             var isset = function(prop) {
3223                 return (typeof prop !== 'undefined');
3224             };
3225
3226             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3227                 return false;
3228             }
3229
3230             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3231
3232
3233             if (isset(attributes[attr]['to'])) {
3234                 end = attributes[attr]['to'];
3235             } else if (isset(attributes[attr]['by'])) {
3236                 if (start.constructor == Array) {
3237                     end = [];
3238                     for (var i = 0, len = start.length; i < len; ++i) {
3239                         end[i] = start[i] + attributes[attr]['by'][i];
3240                     }
3241                 } else {
3242                     end = start + attributes[attr]['by'];
3243                 }
3244             }
3245
3246             this.runtimeAttributes[attr].start = start;
3247             this.runtimeAttributes[attr].end = end;
3248
3249
3250             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3251         },
3252
3253
3254         init: function(el, attributes, duration, method) {
3255
3256             var isAnimated = false;
3257
3258
3259             var startTime = null;
3260
3261
3262             var actualFrames = 0;
3263
3264
3265             el = Roo.getDom(el);
3266
3267
3268             this.attributes = attributes || {};
3269
3270
3271             this.duration = duration || 1;
3272
3273
3274             this.method = method || Roo.lib.Easing.easeNone;
3275
3276
3277             this.useSeconds = true;
3278
3279
3280             this.currentFrame = 0;
3281
3282
3283             this.totalFrames = Roo.lib.AnimMgr.fps;
3284
3285
3286             this.getEl = function() {
3287                 return el;
3288             };
3289
3290
3291             this.isAnimated = function() {
3292                 return isAnimated;
3293             };
3294
3295
3296             this.getStartTime = function() {
3297                 return startTime;
3298             };
3299
3300             this.runtimeAttributes = {};
3301
3302
3303             this.animate = function() {
3304                 if (this.isAnimated()) {
3305                     return false;
3306                 }
3307
3308                 this.currentFrame = 0;
3309
3310                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3311
3312                 Roo.lib.AnimMgr.registerElement(this);
3313             };
3314
3315
3316             this.stop = function(finish) {
3317                 if (finish) {
3318                     this.currentFrame = this.totalFrames;
3319                     this._onTween.fire();
3320                 }
3321                 Roo.lib.AnimMgr.stop(this);
3322             };
3323
3324             var onStart = function() {
3325                 this.onStart.fire();
3326
3327                 this.runtimeAttributes = {};
3328                 for (var attr in this.attributes) {
3329                     this.setRuntimeAttribute(attr);
3330                 }
3331
3332                 isAnimated = true;
3333                 actualFrames = 0;
3334                 startTime = new Date();
3335             };
3336
3337
3338             var onTween = function() {
3339                 var data = {
3340                     duration: new Date() - this.getStartTime(),
3341                     currentFrame: this.currentFrame
3342                 };
3343
3344                 data.toString = function() {
3345                     return (
3346                             'duration: ' + data.duration +
3347                             ', currentFrame: ' + data.currentFrame
3348                             );
3349                 };
3350
3351                 this.onTween.fire(data);
3352
3353                 var runtimeAttributes = this.runtimeAttributes;
3354
3355                 for (var attr in runtimeAttributes) {
3356                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3357                 }
3358
3359                 actualFrames += 1;
3360             };
3361
3362             var onComplete = function() {
3363                 var actual_duration = (new Date() - startTime) / 1000 ;
3364
3365                 var data = {
3366                     duration: actual_duration,
3367                     frames: actualFrames,
3368                     fps: actualFrames / actual_duration
3369                 };
3370
3371                 data.toString = function() {
3372                     return (
3373                             'duration: ' + data.duration +
3374                             ', frames: ' + data.frames +
3375                             ', fps: ' + data.fps
3376                             );
3377                 };
3378
3379                 isAnimated = false;
3380                 actualFrames = 0;
3381                 this.onComplete.fire(data);
3382             };
3383
3384
3385             this._onStart = new Roo.util.Event(this);
3386             this.onStart = new Roo.util.Event(this);
3387             this.onTween = new Roo.util.Event(this);
3388             this._onTween = new Roo.util.Event(this);
3389             this.onComplete = new Roo.util.Event(this);
3390             this._onComplete = new Roo.util.Event(this);
3391             this._onStart.addListener(onStart);
3392             this._onTween.addListener(onTween);
3393             this._onComplete.addListener(onComplete);
3394         }
3395     };
3396 })();
3397 /*
3398  * Portions of this file are based on pieces of Yahoo User Interface Library
3399  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3400  * YUI licensed under the BSD License:
3401  * http://developer.yahoo.net/yui/license.txt
3402  * <script type="text/javascript">
3403  *
3404  */
3405
3406 Roo.lib.AnimMgr = new function() {
3407
3408     var thread = null;
3409
3410
3411     var queue = [];
3412
3413
3414     var tweenCount = 0;
3415
3416
3417     this.fps = 1000;
3418
3419
3420     this.delay = 1;
3421
3422
3423     this.registerElement = function(tween) {
3424         queue[queue.length] = tween;
3425         tweenCount += 1;
3426         tween._onStart.fire();
3427         this.start();
3428     };
3429
3430
3431     this.unRegister = function(tween, index) {
3432         tween._onComplete.fire();
3433         index = index || getIndex(tween);
3434         if (index != -1) {
3435             queue.splice(index, 1);
3436         }
3437
3438         tweenCount -= 1;
3439         if (tweenCount <= 0) {
3440             this.stop();
3441         }
3442     };
3443
3444
3445     this.start = function() {
3446         if (thread === null) {
3447             thread = setInterval(this.run, this.delay);
3448         }
3449     };
3450
3451
3452     this.stop = function(tween) {
3453         if (!tween) {
3454             clearInterval(thread);
3455
3456             for (var i = 0, len = queue.length; i < len; ++i) {
3457                 if (queue[0].isAnimated()) {
3458                     this.unRegister(queue[0], 0);
3459                 }
3460             }
3461
3462             queue = [];
3463             thread = null;
3464             tweenCount = 0;
3465         }
3466         else {
3467             this.unRegister(tween);
3468         }
3469     };
3470
3471
3472     this.run = function() {
3473         for (var i = 0, len = queue.length; i < len; ++i) {
3474             var tween = queue[i];
3475             if (!tween || !tween.isAnimated()) {
3476                 continue;
3477             }
3478
3479             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3480             {
3481                 tween.currentFrame += 1;
3482
3483                 if (tween.useSeconds) {
3484                     correctFrame(tween);
3485                 }
3486                 tween._onTween.fire();
3487             }
3488             else {
3489                 Roo.lib.AnimMgr.stop(tween, i);
3490             }
3491         }
3492     };
3493
3494     var getIndex = function(anim) {
3495         for (var i = 0, len = queue.length; i < len; ++i) {
3496             if (queue[i] == anim) {
3497                 return i;
3498             }
3499         }
3500         return -1;
3501     };
3502
3503
3504     var correctFrame = function(tween) {
3505         var frames = tween.totalFrames;
3506         var frame = tween.currentFrame;
3507         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3508         var elapsed = (new Date() - tween.getStartTime());
3509         var tweak = 0;
3510
3511         if (elapsed < tween.duration * 1000) {
3512             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3513         } else {
3514             tweak = frames - (frame + 1);
3515         }
3516         if (tweak > 0 && isFinite(tweak)) {
3517             if (tween.currentFrame + tweak >= frames) {
3518                 tweak = frames - (frame + 1);
3519             }
3520
3521             tween.currentFrame += tweak;
3522         }
3523     };
3524 };
3525
3526     /*
3527  * Portions of this file are based on pieces of Yahoo User Interface Library
3528  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3529  * YUI licensed under the BSD License:
3530  * http://developer.yahoo.net/yui/license.txt
3531  * <script type="text/javascript">
3532  *
3533  */
3534 Roo.lib.Bezier = new function() {
3535
3536         this.getPosition = function(points, t) {
3537             var n = points.length;
3538             var tmp = [];
3539
3540             for (var i = 0; i < n; ++i) {
3541                 tmp[i] = [points[i][0], points[i][1]];
3542             }
3543
3544             for (var j = 1; j < n; ++j) {
3545                 for (i = 0; i < n - j; ++i) {
3546                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3547                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3548                 }
3549             }
3550
3551             return [ tmp[0][0], tmp[0][1] ];
3552
3553         };
3554     };/*
3555  * Portions of this file are based on pieces of Yahoo User Interface Library
3556  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3557  * YUI licensed under the BSD License:
3558  * http://developer.yahoo.net/yui/license.txt
3559  * <script type="text/javascript">
3560  *
3561  */
3562 (function() {
3563
3564     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3565         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3566     };
3567
3568     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3569
3570     var fly = Roo.lib.AnimBase.fly;
3571     var Y = Roo.lib;
3572     var superclass = Y.ColorAnim.superclass;
3573     var proto = Y.ColorAnim.prototype;
3574
3575     proto.toString = function() {
3576         var el = this.getEl();
3577         var id = el.id || el.tagName;
3578         return ("ColorAnim " + id);
3579     };
3580
3581     proto.patterns.color = /color$/i;
3582     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3583     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3584     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3585     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3586
3587
3588     proto.parseColor = function(s) {
3589         if (s.length == 3) {
3590             return s;
3591         }
3592
3593         var c = this.patterns.hex.exec(s);
3594         if (c && c.length == 4) {
3595             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3596         }
3597
3598         c = this.patterns.rgb.exec(s);
3599         if (c && c.length == 4) {
3600             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3601         }
3602
3603         c = this.patterns.hex3.exec(s);
3604         if (c && c.length == 4) {
3605             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3606         }
3607
3608         return null;
3609     };
3610     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3611     proto.getAttribute = function(attr) {
3612         var el = this.getEl();
3613         if (this.patterns.color.test(attr)) {
3614             var val = fly(el).getStyle(attr);
3615
3616             if (this.patterns.transparent.test(val)) {
3617                 var parent = el.parentNode;
3618                 val = fly(parent).getStyle(attr);
3619
3620                 while (parent && this.patterns.transparent.test(val)) {
3621                     parent = parent.parentNode;
3622                     val = fly(parent).getStyle(attr);
3623                     if (parent.tagName.toUpperCase() == 'HTML') {
3624                         val = '#fff';
3625                     }
3626                 }
3627             }
3628         } else {
3629             val = superclass.getAttribute.call(this, attr);
3630         }
3631
3632         return val;
3633     };
3634     proto.getAttribute = function(attr) {
3635         var el = this.getEl();
3636         if (this.patterns.color.test(attr)) {
3637             var val = fly(el).getStyle(attr);
3638
3639             if (this.patterns.transparent.test(val)) {
3640                 var parent = el.parentNode;
3641                 val = fly(parent).getStyle(attr);
3642
3643                 while (parent && this.patterns.transparent.test(val)) {
3644                     parent = parent.parentNode;
3645                     val = fly(parent).getStyle(attr);
3646                     if (parent.tagName.toUpperCase() == 'HTML') {
3647                         val = '#fff';
3648                     }
3649                 }
3650             }
3651         } else {
3652             val = superclass.getAttribute.call(this, attr);
3653         }
3654
3655         return val;
3656     };
3657
3658     proto.doMethod = function(attr, start, end) {
3659         var val;
3660
3661         if (this.patterns.color.test(attr)) {
3662             val = [];
3663             for (var i = 0, len = start.length; i < len; ++i) {
3664                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3665             }
3666
3667             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3668         }
3669         else {
3670             val = superclass.doMethod.call(this, attr, start, end);
3671         }
3672
3673         return val;
3674     };
3675
3676     proto.setRuntimeAttribute = function(attr) {
3677         superclass.setRuntimeAttribute.call(this, attr);
3678
3679         if (this.patterns.color.test(attr)) {
3680             var attributes = this.attributes;
3681             var start = this.parseColor(this.runtimeAttributes[attr].start);
3682             var end = this.parseColor(this.runtimeAttributes[attr].end);
3683
3684             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3685                 end = this.parseColor(attributes[attr].by);
3686
3687                 for (var i = 0, len = start.length; i < len; ++i) {
3688                     end[i] = start[i] + end[i];
3689                 }
3690             }
3691
3692             this.runtimeAttributes[attr].start = start;
3693             this.runtimeAttributes[attr].end = end;
3694         }
3695     };
3696 })();
3697
3698 /*
3699  * Portions of this file are based on pieces of Yahoo User Interface Library
3700  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3701  * YUI licensed under the BSD License:
3702  * http://developer.yahoo.net/yui/license.txt
3703  * <script type="text/javascript">
3704  *
3705  */
3706 Roo.lib.Easing = {
3707
3708
3709     easeNone: function (t, b, c, d) {
3710         return c * t / d + b;
3711     },
3712
3713
3714     easeIn: function (t, b, c, d) {
3715         return c * (t /= d) * t + b;
3716     },
3717
3718
3719     easeOut: function (t, b, c, d) {
3720         return -c * (t /= d) * (t - 2) + b;
3721     },
3722
3723
3724     easeBoth: function (t, b, c, d) {
3725         if ((t /= d / 2) < 1) {
3726             return c / 2 * t * t + b;
3727         }
3728
3729         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3730     },
3731
3732
3733     easeInStrong: function (t, b, c, d) {
3734         return c * (t /= d) * t * t * t + b;
3735     },
3736
3737
3738     easeOutStrong: function (t, b, c, d) {
3739         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3740     },
3741
3742
3743     easeBothStrong: function (t, b, c, d) {
3744         if ((t /= d / 2) < 1) {
3745             return c / 2 * t * t * t * t + b;
3746         }
3747
3748         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3749     },
3750
3751
3752
3753     elasticIn: function (t, b, c, d, a, p) {
3754         if (t == 0) {
3755             return b;
3756         }
3757         if ((t /= d) == 1) {
3758             return b + c;
3759         }
3760         if (!p) {
3761             p = d * .3;
3762         }
3763
3764         if (!a || a < Math.abs(c)) {
3765             a = c;
3766             var s = p / 4;
3767         }
3768         else {
3769             var s = p / (2 * Math.PI) * Math.asin(c / a);
3770         }
3771
3772         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3773     },
3774
3775
3776     elasticOut: function (t, b, c, d, a, p) {
3777         if (t == 0) {
3778             return b;
3779         }
3780         if ((t /= d) == 1) {
3781             return b + c;
3782         }
3783         if (!p) {
3784             p = d * .3;
3785         }
3786
3787         if (!a || a < Math.abs(c)) {
3788             a = c;
3789             var s = p / 4;
3790         }
3791         else {
3792             var s = p / (2 * Math.PI) * Math.asin(c / a);
3793         }
3794
3795         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3796     },
3797
3798
3799     elasticBoth: function (t, b, c, d, a, p) {
3800         if (t == 0) {
3801             return b;
3802         }
3803
3804         if ((t /= d / 2) == 2) {
3805             return b + c;
3806         }
3807
3808         if (!p) {
3809             p = d * (.3 * 1.5);
3810         }
3811
3812         if (!a || a < Math.abs(c)) {
3813             a = c;
3814             var s = p / 4;
3815         }
3816         else {
3817             var s = p / (2 * Math.PI) * Math.asin(c / a);
3818         }
3819
3820         if (t < 1) {
3821             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3822                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3823         }
3824         return a * Math.pow(2, -10 * (t -= 1)) *
3825                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3826     },
3827
3828
3829
3830     backIn: function (t, b, c, d, s) {
3831         if (typeof s == 'undefined') {
3832             s = 1.70158;
3833         }
3834         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3835     },
3836
3837
3838     backOut: function (t, b, c, d, s) {
3839         if (typeof s == 'undefined') {
3840             s = 1.70158;
3841         }
3842         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3843     },
3844
3845
3846     backBoth: function (t, b, c, d, s) {
3847         if (typeof s == 'undefined') {
3848             s = 1.70158;
3849         }
3850
3851         if ((t /= d / 2 ) < 1) {
3852             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3853         }
3854         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3855     },
3856
3857
3858     bounceIn: function (t, b, c, d) {
3859         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3860     },
3861
3862
3863     bounceOut: function (t, b, c, d) {
3864         if ((t /= d) < (1 / 2.75)) {
3865             return c * (7.5625 * t * t) + b;
3866         } else if (t < (2 / 2.75)) {
3867             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3868         } else if (t < (2.5 / 2.75)) {
3869             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3870         }
3871         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3872     },
3873
3874
3875     bounceBoth: function (t, b, c, d) {
3876         if (t < d / 2) {
3877             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3878         }
3879         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3880     }
3881 };/*
3882  * Portions of this file are based on pieces of Yahoo User Interface Library
3883  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3884  * YUI licensed under the BSD License:
3885  * http://developer.yahoo.net/yui/license.txt
3886  * <script type="text/javascript">
3887  *
3888  */
3889     (function() {
3890         Roo.lib.Motion = function(el, attributes, duration, method) {
3891             if (el) {
3892                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3893             }
3894         };
3895
3896         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3897
3898
3899         var Y = Roo.lib;
3900         var superclass = Y.Motion.superclass;
3901         var proto = Y.Motion.prototype;
3902
3903         proto.toString = function() {
3904             var el = this.getEl();
3905             var id = el.id || el.tagName;
3906             return ("Motion " + id);
3907         };
3908
3909         proto.patterns.points = /^points$/i;
3910
3911         proto.setAttribute = function(attr, val, unit) {
3912             if (this.patterns.points.test(attr)) {
3913                 unit = unit || 'px';
3914                 superclass.setAttribute.call(this, 'left', val[0], unit);
3915                 superclass.setAttribute.call(this, 'top', val[1], unit);
3916             } else {
3917                 superclass.setAttribute.call(this, attr, val, unit);
3918             }
3919         };
3920
3921         proto.getAttribute = function(attr) {
3922             if (this.patterns.points.test(attr)) {
3923                 var val = [
3924                         superclass.getAttribute.call(this, 'left'),
3925                         superclass.getAttribute.call(this, 'top')
3926                         ];
3927             } else {
3928                 val = superclass.getAttribute.call(this, attr);
3929             }
3930
3931             return val;
3932         };
3933
3934         proto.doMethod = function(attr, start, end) {
3935             var val = null;
3936
3937             if (this.patterns.points.test(attr)) {
3938                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3939                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3940             } else {
3941                 val = superclass.doMethod.call(this, attr, start, end);
3942             }
3943             return val;
3944         };
3945
3946         proto.setRuntimeAttribute = function(attr) {
3947             if (this.patterns.points.test(attr)) {
3948                 var el = this.getEl();
3949                 var attributes = this.attributes;
3950                 var start;
3951                 var control = attributes['points']['control'] || [];
3952                 var end;
3953                 var i, len;
3954
3955                 if (control.length > 0 && !(control[0] instanceof Array)) {
3956                     control = [control];
3957                 } else {
3958                     var tmp = [];
3959                     for (i = 0,len = control.length; i < len; ++i) {
3960                         tmp[i] = control[i];
3961                     }
3962                     control = tmp;
3963                 }
3964
3965                 Roo.fly(el).position();
3966
3967                 if (isset(attributes['points']['from'])) {
3968                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3969                 }
3970                 else {
3971                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3972                 }
3973
3974                 start = this.getAttribute('points');
3975
3976
3977                 if (isset(attributes['points']['to'])) {
3978                     end = translateValues.call(this, attributes['points']['to'], start);
3979
3980                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3981                     for (i = 0,len = control.length; i < len; ++i) {
3982                         control[i] = translateValues.call(this, control[i], start);
3983                     }
3984
3985
3986                 } else if (isset(attributes['points']['by'])) {
3987                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3988
3989                     for (i = 0,len = control.length; i < len; ++i) {
3990                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3991                     }
3992                 }
3993
3994                 this.runtimeAttributes[attr] = [start];
3995
3996                 if (control.length > 0) {
3997                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3998                 }
3999
4000                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4001             }
4002             else {
4003                 superclass.setRuntimeAttribute.call(this, attr);
4004             }
4005         };
4006
4007         var translateValues = function(val, start) {
4008             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4009             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4010
4011             return val;
4012         };
4013
4014         var isset = function(prop) {
4015             return (typeof prop !== 'undefined');
4016         };
4017     })();
4018 /*
4019  * Portions of this file are based on pieces of Yahoo User Interface Library
4020  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4021  * YUI licensed under the BSD License:
4022  * http://developer.yahoo.net/yui/license.txt
4023  * <script type="text/javascript">
4024  *
4025  */
4026     (function() {
4027         Roo.lib.Scroll = function(el, attributes, duration, method) {
4028             if (el) {
4029                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4030             }
4031         };
4032
4033         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4034
4035
4036         var Y = Roo.lib;
4037         var superclass = Y.Scroll.superclass;
4038         var proto = Y.Scroll.prototype;
4039
4040         proto.toString = function() {
4041             var el = this.getEl();
4042             var id = el.id || el.tagName;
4043             return ("Scroll " + id);
4044         };
4045
4046         proto.doMethod = function(attr, start, end) {
4047             var val = null;
4048
4049             if (attr == 'scroll') {
4050                 val = [
4051                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4052                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4053                         ];
4054
4055             } else {
4056                 val = superclass.doMethod.call(this, attr, start, end);
4057             }
4058             return val;
4059         };
4060
4061         proto.getAttribute = function(attr) {
4062             var val = null;
4063             var el = this.getEl();
4064
4065             if (attr == 'scroll') {
4066                 val = [ el.scrollLeft, el.scrollTop ];
4067             } else {
4068                 val = superclass.getAttribute.call(this, attr);
4069             }
4070
4071             return val;
4072         };
4073
4074         proto.setAttribute = function(attr, val, unit) {
4075             var el = this.getEl();
4076
4077             if (attr == 'scroll') {
4078                 el.scrollLeft = val[0];
4079                 el.scrollTop = val[1];
4080             } else {
4081                 superclass.setAttribute.call(this, attr, val, unit);
4082             }
4083         };
4084     })();
4085 /*
4086  * Based on:
4087  * Ext JS Library 1.1.1
4088  * Copyright(c) 2006-2007, Ext JS, LLC.
4089  *
4090  * Originally Released Under LGPL - original licence link has changed is not relivant.
4091  *
4092  * Fork - LGPL
4093  * <script type="text/javascript">
4094  */
4095
4096
4097 // nasty IE9 hack - what a pile of crap that is..
4098
4099  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4100     Range.prototype.createContextualFragment = function (html) {
4101         var doc = window.document;
4102         var container = doc.createElement("div");
4103         container.innerHTML = html;
4104         var frag = doc.createDocumentFragment(), n;
4105         while ((n = container.firstChild)) {
4106             frag.appendChild(n);
4107         }
4108         return frag;
4109     };
4110 }
4111
4112 /**
4113  * @class Roo.DomHelper
4114  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4115  * 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>.
4116  * @singleton
4117  */
4118 Roo.DomHelper = function(){
4119     var tempTableEl = null;
4120     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4121     var tableRe = /^table|tbody|tr|td$/i;
4122     var xmlns = {};
4123     // build as innerHTML where available
4124     /** @ignore */
4125     var createHtml = function(o){
4126         if(typeof o == 'string'){
4127             return o;
4128         }
4129         var b = "";
4130         if(!o.tag){
4131             o.tag = "div";
4132         }
4133         b += "<" + o.tag;
4134         for(var attr in o){
4135             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4136             if(attr == "style"){
4137                 var s = o["style"];
4138                 if(typeof s == "function"){
4139                     s = s.call();
4140                 }
4141                 if(typeof s == "string"){
4142                     b += ' style="' + s + '"';
4143                 }else if(typeof s == "object"){
4144                     b += ' style="';
4145                     for(var key in s){
4146                         if(typeof s[key] != "function"){
4147                             b += key + ":" + s[key] + ";";
4148                         }
4149                     }
4150                     b += '"';
4151                 }
4152             }else{
4153                 if(attr == "cls"){
4154                     b += ' class="' + o["cls"] + '"';
4155                 }else if(attr == "htmlFor"){
4156                     b += ' for="' + o["htmlFor"] + '"';
4157                 }else{
4158                     b += " " + attr + '="' + o[attr] + '"';
4159                 }
4160             }
4161         }
4162         if(emptyTags.test(o.tag)){
4163             b += "/>";
4164         }else{
4165             b += ">";
4166             var cn = o.children || o.cn;
4167             if(cn){
4168                 //http://bugs.kde.org/show_bug.cgi?id=71506
4169                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4170                     for(var i = 0, len = cn.length; i < len; i++) {
4171                         b += createHtml(cn[i], b);
4172                     }
4173                 }else{
4174                     b += createHtml(cn, b);
4175                 }
4176             }
4177             if(o.html){
4178                 b += o.html;
4179             }
4180             b += "</" + o.tag + ">";
4181         }
4182         return b;
4183     };
4184
4185     // build as dom
4186     /** @ignore */
4187     var createDom = function(o, parentNode){
4188          
4189         // defininition craeted..
4190         var ns = false;
4191         if (o.ns && o.ns != 'html') {
4192                
4193             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4194                 xmlns[o.ns] = o.xmlns;
4195                 ns = o.xmlns;
4196             }
4197             if (typeof(xmlns[o.ns]) == 'undefined') {
4198                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4199             }
4200             ns = xmlns[o.ns];
4201         }
4202         
4203         
4204         if (typeof(o) == 'string') {
4205             return parentNode.appendChild(document.createTextNode(o));
4206         }
4207         o.tag = o.tag || div;
4208         if (o.ns && Roo.isIE) {
4209             ns = false;
4210             o.tag = o.ns + ':' + o.tag;
4211             
4212         }
4213         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4214         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4215         for(var attr in o){
4216             
4217             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4218                     attr == "style" || typeof o[attr] == "function") continue;
4219                     
4220             if(attr=="cls" && Roo.isIE){
4221                 el.className = o["cls"];
4222             }else{
4223                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4224                 else el[attr] = o[attr];
4225             }
4226         }
4227         Roo.DomHelper.applyStyles(el, o.style);
4228         var cn = o.children || o.cn;
4229         if(cn){
4230             //http://bugs.kde.org/show_bug.cgi?id=71506
4231              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4232                 for(var i = 0, len = cn.length; i < len; i++) {
4233                     createDom(cn[i], el);
4234                 }
4235             }else{
4236                 createDom(cn, el);
4237             }
4238         }
4239         if(o.html){
4240             el.innerHTML = o.html;
4241         }
4242         if(parentNode){
4243            parentNode.appendChild(el);
4244         }
4245         return el;
4246     };
4247
4248     var ieTable = function(depth, s, h, e){
4249         tempTableEl.innerHTML = [s, h, e].join('');
4250         var i = -1, el = tempTableEl;
4251         while(++i < depth){
4252             el = el.firstChild;
4253         }
4254         return el;
4255     };
4256
4257     // kill repeat to save bytes
4258     var ts = '<table>',
4259         te = '</table>',
4260         tbs = ts+'<tbody>',
4261         tbe = '</tbody>'+te,
4262         trs = tbs + '<tr>',
4263         tre = '</tr>'+tbe;
4264
4265     /**
4266      * @ignore
4267      * Nasty code for IE's broken table implementation
4268      */
4269     var insertIntoTable = function(tag, where, el, html){
4270         if(!tempTableEl){
4271             tempTableEl = document.createElement('div');
4272         }
4273         var node;
4274         var before = null;
4275         if(tag == 'td'){
4276             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4277                 return;
4278             }
4279             if(where == 'beforebegin'){
4280                 before = el;
4281                 el = el.parentNode;
4282             } else{
4283                 before = el.nextSibling;
4284                 el = el.parentNode;
4285             }
4286             node = ieTable(4, trs, html, tre);
4287         }
4288         else if(tag == 'tr'){
4289             if(where == 'beforebegin'){
4290                 before = el;
4291                 el = el.parentNode;
4292                 node = ieTable(3, tbs, html, tbe);
4293             } else if(where == 'afterend'){
4294                 before = el.nextSibling;
4295                 el = el.parentNode;
4296                 node = ieTable(3, tbs, html, tbe);
4297             } else{ // INTO a TR
4298                 if(where == 'afterbegin'){
4299                     before = el.firstChild;
4300                 }
4301                 node = ieTable(4, trs, html, tre);
4302             }
4303         } else if(tag == 'tbody'){
4304             if(where == 'beforebegin'){
4305                 before = el;
4306                 el = el.parentNode;
4307                 node = ieTable(2, ts, html, te);
4308             } else if(where == 'afterend'){
4309                 before = el.nextSibling;
4310                 el = el.parentNode;
4311                 node = ieTable(2, ts, html, te);
4312             } else{
4313                 if(where == 'afterbegin'){
4314                     before = el.firstChild;
4315                 }
4316                 node = ieTable(3, tbs, html, tbe);
4317             }
4318         } else{ // TABLE
4319             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4320                 return;
4321             }
4322             if(where == 'afterbegin'){
4323                 before = el.firstChild;
4324             }
4325             node = ieTable(2, ts, html, te);
4326         }
4327         el.insertBefore(node, before);
4328         return node;
4329     };
4330
4331     return {
4332     /** True to force the use of DOM instead of html fragments @type Boolean */
4333     useDom : false,
4334
4335     /**
4336      * Returns the markup for the passed Element(s) config
4337      * @param {Object} o The Dom object spec (and children)
4338      * @return {String}
4339      */
4340     markup : function(o){
4341         return createHtml(o);
4342     },
4343
4344     /**
4345      * Applies a style specification to an element
4346      * @param {String/HTMLElement} el The element to apply styles to
4347      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4348      * a function which returns such a specification.
4349      */
4350     applyStyles : function(el, styles){
4351         if(styles){
4352            el = Roo.fly(el);
4353            if(typeof styles == "string"){
4354                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4355                var matches;
4356                while ((matches = re.exec(styles)) != null){
4357                    el.setStyle(matches[1], matches[2]);
4358                }
4359            }else if (typeof styles == "object"){
4360                for (var style in styles){
4361                   el.setStyle(style, styles[style]);
4362                }
4363            }else if (typeof styles == "function"){
4364                 Roo.DomHelper.applyStyles(el, styles.call());
4365            }
4366         }
4367     },
4368
4369     /**
4370      * Inserts an HTML fragment into the Dom
4371      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4372      * @param {HTMLElement} el The context element
4373      * @param {String} html The HTML fragmenet
4374      * @return {HTMLElement} The new node
4375      */
4376     insertHtml : function(where, el, html){
4377         where = where.toLowerCase();
4378         if(el.insertAdjacentHTML){
4379             if(tableRe.test(el.tagName)){
4380                 var rs;
4381                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4382                     return rs;
4383                 }
4384             }
4385             switch(where){
4386                 case "beforebegin":
4387                     el.insertAdjacentHTML('BeforeBegin', html);
4388                     return el.previousSibling;
4389                 case "afterbegin":
4390                     el.insertAdjacentHTML('AfterBegin', html);
4391                     return el.firstChild;
4392                 case "beforeend":
4393                     el.insertAdjacentHTML('BeforeEnd', html);
4394                     return el.lastChild;
4395                 case "afterend":
4396                     el.insertAdjacentHTML('AfterEnd', html);
4397                     return el.nextSibling;
4398             }
4399             throw 'Illegal insertion point -> "' + where + '"';
4400         }
4401         var range = el.ownerDocument.createRange();
4402         var frag;
4403         switch(where){
4404              case "beforebegin":
4405                 range.setStartBefore(el);
4406                 frag = range.createContextualFragment(html);
4407                 el.parentNode.insertBefore(frag, el);
4408                 return el.previousSibling;
4409              case "afterbegin":
4410                 if(el.firstChild){
4411                     range.setStartBefore(el.firstChild);
4412                     frag = range.createContextualFragment(html);
4413                     el.insertBefore(frag, el.firstChild);
4414                     return el.firstChild;
4415                 }else{
4416                     el.innerHTML = html;
4417                     return el.firstChild;
4418                 }
4419             case "beforeend":
4420                 if(el.lastChild){
4421                     range.setStartAfter(el.lastChild);
4422                     frag = range.createContextualFragment(html);
4423                     el.appendChild(frag);
4424                     return el.lastChild;
4425                 }else{
4426                     el.innerHTML = html;
4427                     return el.lastChild;
4428                 }
4429             case "afterend":
4430                 range.setStartAfter(el);
4431                 frag = range.createContextualFragment(html);
4432                 el.parentNode.insertBefore(frag, el.nextSibling);
4433                 return el.nextSibling;
4434             }
4435             throw 'Illegal insertion point -> "' + where + '"';
4436     },
4437
4438     /**
4439      * Creates new Dom element(s) and inserts them before el
4440      * @param {String/HTMLElement/Element} el The context element
4441      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4442      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4443      * @return {HTMLElement/Roo.Element} The new node
4444      */
4445     insertBefore : function(el, o, returnElement){
4446         return this.doInsert(el, o, returnElement, "beforeBegin");
4447     },
4448
4449     /**
4450      * Creates new Dom element(s) and inserts them after el
4451      * @param {String/HTMLElement/Element} el The context element
4452      * @param {Object} o The Dom object spec (and children)
4453      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4454      * @return {HTMLElement/Roo.Element} The new node
4455      */
4456     insertAfter : function(el, o, returnElement){
4457         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4458     },
4459
4460     /**
4461      * Creates new Dom element(s) and inserts them as the first child of el
4462      * @param {String/HTMLElement/Element} el The context element
4463      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4464      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4465      * @return {HTMLElement/Roo.Element} The new node
4466      */
4467     insertFirst : function(el, o, returnElement){
4468         return this.doInsert(el, o, returnElement, "afterBegin");
4469     },
4470
4471     // private
4472     doInsert : function(el, o, returnElement, pos, sibling){
4473         el = Roo.getDom(el);
4474         var newNode;
4475         if(this.useDom || o.ns){
4476             newNode = createDom(o, null);
4477             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4478         }else{
4479             var html = createHtml(o);
4480             newNode = this.insertHtml(pos, el, html);
4481         }
4482         return returnElement ? Roo.get(newNode, true) : newNode;
4483     },
4484
4485     /**
4486      * Creates new Dom element(s) and appends them to el
4487      * @param {String/HTMLElement/Element} el The context element
4488      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4489      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4490      * @return {HTMLElement/Roo.Element} The new node
4491      */
4492     append : function(el, o, returnElement){
4493         el = Roo.getDom(el);
4494         var newNode;
4495         if(this.useDom || o.ns){
4496             newNode = createDom(o, null);
4497             el.appendChild(newNode);
4498         }else{
4499             var html = createHtml(o);
4500             newNode = this.insertHtml("beforeEnd", el, html);
4501         }
4502         return returnElement ? Roo.get(newNode, true) : newNode;
4503     },
4504
4505     /**
4506      * Creates new Dom element(s) and overwrites the contents of el with them
4507      * @param {String/HTMLElement/Element} el The context element
4508      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4509      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4510      * @return {HTMLElement/Roo.Element} The new node
4511      */
4512     overwrite : function(el, o, returnElement){
4513         el = Roo.getDom(el);
4514         if (o.ns) {
4515           
4516             while (el.childNodes.length) {
4517                 el.removeChild(el.firstChild);
4518             }
4519             createDom(o, el);
4520         } else {
4521             el.innerHTML = createHtml(o);   
4522         }
4523         
4524         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4525     },
4526
4527     /**
4528      * Creates a new Roo.DomHelper.Template from the Dom object spec
4529      * @param {Object} o The Dom object spec (and children)
4530      * @return {Roo.DomHelper.Template} The new template
4531      */
4532     createTemplate : function(o){
4533         var html = createHtml(o);
4534         return new Roo.Template(html);
4535     }
4536     };
4537 }();
4538 /*
4539  * Based on:
4540  * Ext JS Library 1.1.1
4541  * Copyright(c) 2006-2007, Ext JS, LLC.
4542  *
4543  * Originally Released Under LGPL - original licence link has changed is not relivant.
4544  *
4545  * Fork - LGPL
4546  * <script type="text/javascript">
4547  */
4548  
4549 /**
4550 * @class Roo.Template
4551 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4552 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4553 * Usage:
4554 <pre><code>
4555 var t = new Roo.Template({
4556     html :  '&lt;div name="{id}"&gt;' + 
4557         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4558         '&lt;/div&gt;',
4559     myformat: function (value, allValues) {
4560         return 'XX' + value;
4561     }
4562 });
4563 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4564 </code></pre>
4565 * For more information see this blog post with examples:
4566 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4567      - Create Elements using DOM, HTML fragments and Templates</a>. 
4568 * @constructor
4569 * @param {Object} cfg - Configuration object.
4570 */
4571 Roo.Template = function(cfg){
4572     // BC!
4573     if(cfg instanceof Array){
4574         cfg = cfg.join("");
4575     }else if(arguments.length > 1){
4576         cfg = Array.prototype.join.call(arguments, "");
4577     }
4578     
4579     
4580     if (typeof(cfg) == 'object') {
4581         Roo.apply(this,cfg)
4582     } else {
4583         // bc
4584         this.html = cfg;
4585     }
4586     if (this.url) {
4587         this.load();
4588     }
4589     
4590 };
4591 Roo.Template.prototype = {
4592     
4593     /**
4594      * @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..
4595      *                    it should be fixed so that template is observable...
4596      */
4597     url : false,
4598     /**
4599      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4600      */
4601     html : '',
4602     /**
4603      * Returns an HTML fragment of this template with the specified values applied.
4604      * @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'})
4605      * @return {String} The HTML fragment
4606      */
4607     applyTemplate : function(values){
4608         try {
4609            
4610             if(this.compiled){
4611                 return this.compiled(values);
4612             }
4613             var useF = this.disableFormats !== true;
4614             var fm = Roo.util.Format, tpl = this;
4615             var fn = function(m, name, format, args){
4616                 if(format && useF){
4617                     if(format.substr(0, 5) == "this."){
4618                         return tpl.call(format.substr(5), values[name], values);
4619                     }else{
4620                         if(args){
4621                             // quoted values are required for strings in compiled templates, 
4622                             // but for non compiled we need to strip them
4623                             // quoted reversed for jsmin
4624                             var re = /^\s*['"](.*)["']\s*$/;
4625                             args = args.split(',');
4626                             for(var i = 0, len = args.length; i < len; i++){
4627                                 args[i] = args[i].replace(re, "$1");
4628                             }
4629                             args = [values[name]].concat(args);
4630                         }else{
4631                             args = [values[name]];
4632                         }
4633                         return fm[format].apply(fm, args);
4634                     }
4635                 }else{
4636                     return values[name] !== undefined ? values[name] : "";
4637                 }
4638             };
4639             return this.html.replace(this.re, fn);
4640         } catch (e) {
4641             Roo.log(e);
4642             throw e;
4643         }
4644          
4645     },
4646     
4647     loading : false,
4648       
4649     load : function ()
4650     {
4651          
4652         if (this.loading) {
4653             return;
4654         }
4655         var _t = this;
4656         
4657         this.loading = true;
4658         this.compiled = false;
4659         
4660         var cx = new Roo.data.Connection();
4661         cx.request({
4662             url : this.url,
4663             method : 'GET',
4664             success : function (response) {
4665                 _t.loading = false;
4666                 _t.html = response.responseText;
4667                 _t.url = false;
4668                 _t.compile();
4669              },
4670             failure : function(response) {
4671                 Roo.log("Template failed to load from " + _t.url);
4672                 _t.loading = false;
4673             }
4674         });
4675     },
4676
4677     /**
4678      * Sets the HTML used as the template and optionally compiles it.
4679      * @param {String} html
4680      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4681      * @return {Roo.Template} this
4682      */
4683     set : function(html, compile){
4684         this.html = html;
4685         this.compiled = null;
4686         if(compile){
4687             this.compile();
4688         }
4689         return this;
4690     },
4691     
4692     /**
4693      * True to disable format functions (defaults to false)
4694      * @type Boolean
4695      */
4696     disableFormats : false,
4697     
4698     /**
4699     * The regular expression used to match template variables 
4700     * @type RegExp
4701     * @property 
4702     */
4703     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4704     
4705     /**
4706      * Compiles the template into an internal function, eliminating the RegEx overhead.
4707      * @return {Roo.Template} this
4708      */
4709     compile : function(){
4710         var fm = Roo.util.Format;
4711         var useF = this.disableFormats !== true;
4712         var sep = Roo.isGecko ? "+" : ",";
4713         var fn = function(m, name, format, args){
4714             if(format && useF){
4715                 args = args ? ',' + args : "";
4716                 if(format.substr(0, 5) != "this."){
4717                     format = "fm." + format + '(';
4718                 }else{
4719                     format = 'this.call("'+ format.substr(5) + '", ';
4720                     args = ", values";
4721                 }
4722             }else{
4723                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4724             }
4725             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4726         };
4727         var body;
4728         // branched to use + in gecko and [].join() in others
4729         if(Roo.isGecko){
4730             body = "this.compiled = function(values){ return '" +
4731                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4732                     "';};";
4733         }else{
4734             body = ["this.compiled = function(values){ return ['"];
4735             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4736             body.push("'].join('');};");
4737             body = body.join('');
4738         }
4739         /**
4740          * eval:var:values
4741          * eval:var:fm
4742          */
4743         eval(body);
4744         return this;
4745     },
4746     
4747     // private function used to call members
4748     call : function(fnName, value, allValues){
4749         return this[fnName](value, allValues);
4750     },
4751     
4752     /**
4753      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4754      * @param {String/HTMLElement/Roo.Element} el The context element
4755      * @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'})
4756      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4757      * @return {HTMLElement/Roo.Element} The new node or Element
4758      */
4759     insertFirst: function(el, values, returnElement){
4760         return this.doInsert('afterBegin', el, values, returnElement);
4761     },
4762
4763     /**
4764      * Applies the supplied values to the template and inserts the new node(s) before el.
4765      * @param {String/HTMLElement/Roo.Element} el The context element
4766      * @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'})
4767      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4768      * @return {HTMLElement/Roo.Element} The new node or Element
4769      */
4770     insertBefore: function(el, values, returnElement){
4771         return this.doInsert('beforeBegin', el, values, returnElement);
4772     },
4773
4774     /**
4775      * Applies the supplied values to the template and inserts the new node(s) after el.
4776      * @param {String/HTMLElement/Roo.Element} el The context element
4777      * @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'})
4778      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4779      * @return {HTMLElement/Roo.Element} The new node or Element
4780      */
4781     insertAfter : function(el, values, returnElement){
4782         return this.doInsert('afterEnd', el, values, returnElement);
4783     },
4784     
4785     /**
4786      * Applies the supplied values to the template and appends the new node(s) to el.
4787      * @param {String/HTMLElement/Roo.Element} el The context element
4788      * @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'})
4789      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4790      * @return {HTMLElement/Roo.Element} The new node or Element
4791      */
4792     append : function(el, values, returnElement){
4793         return this.doInsert('beforeEnd', el, values, returnElement);
4794     },
4795
4796     doInsert : function(where, el, values, returnEl){
4797         el = Roo.getDom(el);
4798         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4799         return returnEl ? Roo.get(newNode, true) : newNode;
4800     },
4801
4802     /**
4803      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4804      * @param {String/HTMLElement/Roo.Element} el The context element
4805      * @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'})
4806      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4807      * @return {HTMLElement/Roo.Element} The new node or Element
4808      */
4809     overwrite : function(el, values, returnElement){
4810         el = Roo.getDom(el);
4811         el.innerHTML = this.applyTemplate(values);
4812         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4813     }
4814 };
4815 /**
4816  * Alias for {@link #applyTemplate}
4817  * @method
4818  */
4819 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4820
4821 // backwards compat
4822 Roo.DomHelper.Template = Roo.Template;
4823
4824 /**
4825  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4826  * @param {String/HTMLElement} el A DOM element or its id
4827  * @returns {Roo.Template} The created template
4828  * @static
4829  */
4830 Roo.Template.from = function(el){
4831     el = Roo.getDom(el);
4832     return new Roo.Template(el.value || el.innerHTML);
4833 };/*
4834  * Based on:
4835  * Ext JS Library 1.1.1
4836  * Copyright(c) 2006-2007, Ext JS, LLC.
4837  *
4838  * Originally Released Under LGPL - original licence link has changed is not relivant.
4839  *
4840  * Fork - LGPL
4841  * <script type="text/javascript">
4842  */
4843  
4844
4845 /*
4846  * This is code is also distributed under MIT license for use
4847  * with jQuery and prototype JavaScript libraries.
4848  */
4849 /**
4850  * @class Roo.DomQuery
4851 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).
4852 <p>
4853 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>
4854
4855 <p>
4856 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.
4857 </p>
4858 <h4>Element Selectors:</h4>
4859 <ul class="list">
4860     <li> <b>*</b> any element</li>
4861     <li> <b>E</b> an element with the tag E</li>
4862     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4863     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4864     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4865     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4866 </ul>
4867 <h4>Attribute Selectors:</h4>
4868 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4869 <ul class="list">
4870     <li> <b>E[foo]</b> has an attribute "foo"</li>
4871     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4872     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4873     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4874     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4875     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4876     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4877 </ul>
4878 <h4>Pseudo Classes:</h4>
4879 <ul class="list">
4880     <li> <b>E:first-child</b> E is the first child of its parent</li>
4881     <li> <b>E:last-child</b> E is the last child of its parent</li>
4882     <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>
4883     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4884     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4885     <li> <b>E:only-child</b> E is the only child of its parent</li>
4886     <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>
4887     <li> <b>E:first</b> the first E in the resultset</li>
4888     <li> <b>E:last</b> the last E in the resultset</li>
4889     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4890     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4891     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4892     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4893     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4894     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4895     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4896     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4897     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4898 </ul>
4899 <h4>CSS Value Selectors:</h4>
4900 <ul class="list">
4901     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4902     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4903     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4904     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4905     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4906     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4907 </ul>
4908  * @singleton
4909  */
4910 Roo.DomQuery = function(){
4911     var cache = {}, simpleCache = {}, valueCache = {};
4912     var nonSpace = /\S/;
4913     var trimRe = /^\s+|\s+$/g;
4914     var tplRe = /\{(\d+)\}/g;
4915     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4916     var tagTokenRe = /^(#)?([\w-\*]+)/;
4917     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4918
4919     function child(p, index){
4920         var i = 0;
4921         var n = p.firstChild;
4922         while(n){
4923             if(n.nodeType == 1){
4924                if(++i == index){
4925                    return n;
4926                }
4927             }
4928             n = n.nextSibling;
4929         }
4930         return null;
4931     };
4932
4933     function next(n){
4934         while((n = n.nextSibling) && n.nodeType != 1);
4935         return n;
4936     };
4937
4938     function prev(n){
4939         while((n = n.previousSibling) && n.nodeType != 1);
4940         return n;
4941     };
4942
4943     function children(d){
4944         var n = d.firstChild, ni = -1;
4945             while(n){
4946                 var nx = n.nextSibling;
4947                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4948                     d.removeChild(n);
4949                 }else{
4950                     n.nodeIndex = ++ni;
4951                 }
4952                 n = nx;
4953             }
4954             return this;
4955         };
4956
4957     function byClassName(c, a, v){
4958         if(!v){
4959             return c;
4960         }
4961         var r = [], ri = -1, cn;
4962         for(var i = 0, ci; ci = c[i]; i++){
4963             if((' '+ci.className+' ').indexOf(v) != -1){
4964                 r[++ri] = ci;
4965             }
4966         }
4967         return r;
4968     };
4969
4970     function attrValue(n, attr){
4971         if(!n.tagName && typeof n.length != "undefined"){
4972             n = n[0];
4973         }
4974         if(!n){
4975             return null;
4976         }
4977         if(attr == "for"){
4978             return n.htmlFor;
4979         }
4980         if(attr == "class" || attr == "className"){
4981             return n.className;
4982         }
4983         return n.getAttribute(attr) || n[attr];
4984
4985     };
4986
4987     function getNodes(ns, mode, tagName){
4988         var result = [], ri = -1, cs;
4989         if(!ns){
4990             return result;
4991         }
4992         tagName = tagName || "*";
4993         if(typeof ns.getElementsByTagName != "undefined"){
4994             ns = [ns];
4995         }
4996         if(!mode){
4997             for(var i = 0, ni; ni = ns[i]; i++){
4998                 cs = ni.getElementsByTagName(tagName);
4999                 for(var j = 0, ci; ci = cs[j]; j++){
5000                     result[++ri] = ci;
5001                 }
5002             }
5003         }else if(mode == "/" || mode == ">"){
5004             var utag = tagName.toUpperCase();
5005             for(var i = 0, ni, cn; ni = ns[i]; i++){
5006                 cn = ni.children || ni.childNodes;
5007                 for(var j = 0, cj; cj = cn[j]; j++){
5008                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5009                         result[++ri] = cj;
5010                     }
5011                 }
5012             }
5013         }else if(mode == "+"){
5014             var utag = tagName.toUpperCase();
5015             for(var i = 0, n; n = ns[i]; i++){
5016                 while((n = n.nextSibling) && n.nodeType != 1);
5017                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5018                     result[++ri] = n;
5019                 }
5020             }
5021         }else if(mode == "~"){
5022             for(var i = 0, n; n = ns[i]; i++){
5023                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5024                 if(n){
5025                     result[++ri] = n;
5026                 }
5027             }
5028         }
5029         return result;
5030     };
5031
5032     function concat(a, b){
5033         if(b.slice){
5034             return a.concat(b);
5035         }
5036         for(var i = 0, l = b.length; i < l; i++){
5037             a[a.length] = b[i];
5038         }
5039         return a;
5040     }
5041
5042     function byTag(cs, tagName){
5043         if(cs.tagName || cs == document){
5044             cs = [cs];
5045         }
5046         if(!tagName){
5047             return cs;
5048         }
5049         var r = [], ri = -1;
5050         tagName = tagName.toLowerCase();
5051         for(var i = 0, ci; ci = cs[i]; i++){
5052             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5053                 r[++ri] = ci;
5054             }
5055         }
5056         return r;
5057     };
5058
5059     function byId(cs, attr, id){
5060         if(cs.tagName || cs == document){
5061             cs = [cs];
5062         }
5063         if(!id){
5064             return cs;
5065         }
5066         var r = [], ri = -1;
5067         for(var i = 0,ci; ci = cs[i]; i++){
5068             if(ci && ci.id == id){
5069                 r[++ri] = ci;
5070                 return r;
5071             }
5072         }
5073         return r;
5074     };
5075
5076     function byAttribute(cs, attr, value, op, custom){
5077         var r = [], ri = -1, st = custom=="{";
5078         var f = Roo.DomQuery.operators[op];
5079         for(var i = 0, ci; ci = cs[i]; i++){
5080             var a;
5081             if(st){
5082                 a = Roo.DomQuery.getStyle(ci, attr);
5083             }
5084             else if(attr == "class" || attr == "className"){
5085                 a = ci.className;
5086             }else if(attr == "for"){
5087                 a = ci.htmlFor;
5088             }else if(attr == "href"){
5089                 a = ci.getAttribute("href", 2);
5090             }else{
5091                 a = ci.getAttribute(attr);
5092             }
5093             if((f && f(a, value)) || (!f && a)){
5094                 r[++ri] = ci;
5095             }
5096         }
5097         return r;
5098     };
5099
5100     function byPseudo(cs, name, value){
5101         return Roo.DomQuery.pseudos[name](cs, value);
5102     };
5103
5104     // This is for IE MSXML which does not support expandos.
5105     // IE runs the same speed using setAttribute, however FF slows way down
5106     // and Safari completely fails so they need to continue to use expandos.
5107     var isIE = window.ActiveXObject ? true : false;
5108
5109     // this eval is stop the compressor from
5110     // renaming the variable to something shorter
5111     
5112     /** eval:var:batch */
5113     var batch = 30803; 
5114
5115     var key = 30803;
5116
5117     function nodupIEXml(cs){
5118         var d = ++key;
5119         cs[0].setAttribute("_nodup", d);
5120         var r = [cs[0]];
5121         for(var i = 1, len = cs.length; i < len; i++){
5122             var c = cs[i];
5123             if(!c.getAttribute("_nodup") != d){
5124                 c.setAttribute("_nodup", d);
5125                 r[r.length] = c;
5126             }
5127         }
5128         for(var i = 0, len = cs.length; i < len; i++){
5129             cs[i].removeAttribute("_nodup");
5130         }
5131         return r;
5132     }
5133
5134     function nodup(cs){
5135         if(!cs){
5136             return [];
5137         }
5138         var len = cs.length, c, i, r = cs, cj, ri = -1;
5139         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5140             return cs;
5141         }
5142         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5143             return nodupIEXml(cs);
5144         }
5145         var d = ++key;
5146         cs[0]._nodup = d;
5147         for(i = 1; c = cs[i]; i++){
5148             if(c._nodup != d){
5149                 c._nodup = d;
5150             }else{
5151                 r = [];
5152                 for(var j = 0; j < i; j++){
5153                     r[++ri] = cs[j];
5154                 }
5155                 for(j = i+1; cj = cs[j]; j++){
5156                     if(cj._nodup != d){
5157                         cj._nodup = d;
5158                         r[++ri] = cj;
5159                     }
5160                 }
5161                 return r;
5162             }
5163         }
5164         return r;
5165     }
5166
5167     function quickDiffIEXml(c1, c2){
5168         var d = ++key;
5169         for(var i = 0, len = c1.length; i < len; i++){
5170             c1[i].setAttribute("_qdiff", d);
5171         }
5172         var r = [];
5173         for(var i = 0, len = c2.length; i < len; i++){
5174             if(c2[i].getAttribute("_qdiff") != d){
5175                 r[r.length] = c2[i];
5176             }
5177         }
5178         for(var i = 0, len = c1.length; i < len; i++){
5179            c1[i].removeAttribute("_qdiff");
5180         }
5181         return r;
5182     }
5183
5184     function quickDiff(c1, c2){
5185         var len1 = c1.length;
5186         if(!len1){
5187             return c2;
5188         }
5189         if(isIE && c1[0].selectSingleNode){
5190             return quickDiffIEXml(c1, c2);
5191         }
5192         var d = ++key;
5193         for(var i = 0; i < len1; i++){
5194             c1[i]._qdiff = d;
5195         }
5196         var r = [];
5197         for(var i = 0, len = c2.length; i < len; i++){
5198             if(c2[i]._qdiff != d){
5199                 r[r.length] = c2[i];
5200             }
5201         }
5202         return r;
5203     }
5204
5205     function quickId(ns, mode, root, id){
5206         if(ns == root){
5207            var d = root.ownerDocument || root;
5208            return d.getElementById(id);
5209         }
5210         ns = getNodes(ns, mode, "*");
5211         return byId(ns, null, id);
5212     }
5213
5214     return {
5215         getStyle : function(el, name){
5216             return Roo.fly(el).getStyle(name);
5217         },
5218         /**
5219          * Compiles a selector/xpath query into a reusable function. The returned function
5220          * takes one parameter "root" (optional), which is the context node from where the query should start.
5221          * @param {String} selector The selector/xpath query
5222          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5223          * @return {Function}
5224          */
5225         compile : function(path, type){
5226             type = type || "select";
5227             
5228             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5229             var q = path, mode, lq;
5230             var tk = Roo.DomQuery.matchers;
5231             var tklen = tk.length;
5232             var mm;
5233
5234             // accept leading mode switch
5235             var lmode = q.match(modeRe);
5236             if(lmode && lmode[1]){
5237                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5238                 q = q.replace(lmode[1], "");
5239             }
5240             // strip leading slashes
5241             while(path.substr(0, 1)=="/"){
5242                 path = path.substr(1);
5243             }
5244
5245             while(q && lq != q){
5246                 lq = q;
5247                 var tm = q.match(tagTokenRe);
5248                 if(type == "select"){
5249                     if(tm){
5250                         if(tm[1] == "#"){
5251                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5252                         }else{
5253                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5254                         }
5255                         q = q.replace(tm[0], "");
5256                     }else if(q.substr(0, 1) != '@'){
5257                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5258                     }
5259                 }else{
5260                     if(tm){
5261                         if(tm[1] == "#"){
5262                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5263                         }else{
5264                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5265                         }
5266                         q = q.replace(tm[0], "");
5267                     }
5268                 }
5269                 while(!(mm = q.match(modeRe))){
5270                     var matched = false;
5271                     for(var j = 0; j < tklen; j++){
5272                         var t = tk[j];
5273                         var m = q.match(t.re);
5274                         if(m){
5275                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5276                                                     return m[i];
5277                                                 });
5278                             q = q.replace(m[0], "");
5279                             matched = true;
5280                             break;
5281                         }
5282                     }
5283                     // prevent infinite loop on bad selector
5284                     if(!matched){
5285                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5286                     }
5287                 }
5288                 if(mm[1]){
5289                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5290                     q = q.replace(mm[1], "");
5291                 }
5292             }
5293             fn[fn.length] = "return nodup(n);\n}";
5294             
5295              /** 
5296               * list of variables that need from compression as they are used by eval.
5297              *  eval:var:batch 
5298              *  eval:var:nodup
5299              *  eval:var:byTag
5300              *  eval:var:ById
5301              *  eval:var:getNodes
5302              *  eval:var:quickId
5303              *  eval:var:mode
5304              *  eval:var:root
5305              *  eval:var:n
5306              *  eval:var:byClassName
5307              *  eval:var:byPseudo
5308              *  eval:var:byAttribute
5309              *  eval:var:attrValue
5310              * 
5311              **/ 
5312             eval(fn.join(""));
5313             return f;
5314         },
5315
5316         /**
5317          * Selects a group of elements.
5318          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5319          * @param {Node} root (optional) The start of the query (defaults to document).
5320          * @return {Array}
5321          */
5322         select : function(path, root, type){
5323             if(!root || root == document){
5324                 root = document;
5325             }
5326             if(typeof root == "string"){
5327                 root = document.getElementById(root);
5328             }
5329             var paths = path.split(",");
5330             var results = [];
5331             for(var i = 0, len = paths.length; i < len; i++){
5332                 var p = paths[i].replace(trimRe, "");
5333                 if(!cache[p]){
5334                     cache[p] = Roo.DomQuery.compile(p);
5335                     if(!cache[p]){
5336                         throw p + " is not a valid selector";
5337                     }
5338                 }
5339                 var result = cache[p](root);
5340                 if(result && result != document){
5341                     results = results.concat(result);
5342                 }
5343             }
5344             if(paths.length > 1){
5345                 return nodup(results);
5346             }
5347             return results;
5348         },
5349
5350         /**
5351          * Selects a single element.
5352          * @param {String} selector The selector/xpath query
5353          * @param {Node} root (optional) The start of the query (defaults to document).
5354          * @return {Element}
5355          */
5356         selectNode : function(path, root){
5357             return Roo.DomQuery.select(path, root)[0];
5358         },
5359
5360         /**
5361          * Selects the value of a node, optionally replacing null with the defaultValue.
5362          * @param {String} selector The selector/xpath query
5363          * @param {Node} root (optional) The start of the query (defaults to document).
5364          * @param {String} defaultValue
5365          */
5366         selectValue : function(path, root, defaultValue){
5367             path = path.replace(trimRe, "");
5368             if(!valueCache[path]){
5369                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5370             }
5371             var n = valueCache[path](root);
5372             n = n[0] ? n[0] : n;
5373             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5374             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5375         },
5376
5377         /**
5378          * Selects the value of a node, parsing integers and floats.
5379          * @param {String} selector The selector/xpath query
5380          * @param {Node} root (optional) The start of the query (defaults to document).
5381          * @param {Number} defaultValue
5382          * @return {Number}
5383          */
5384         selectNumber : function(path, root, defaultValue){
5385             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5386             return parseFloat(v);
5387         },
5388
5389         /**
5390          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5391          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5392          * @param {String} selector The simple selector to test
5393          * @return {Boolean}
5394          */
5395         is : function(el, ss){
5396             if(typeof el == "string"){
5397                 el = document.getElementById(el);
5398             }
5399             var isArray = (el instanceof Array);
5400             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5401             return isArray ? (result.length == el.length) : (result.length > 0);
5402         },
5403
5404         /**
5405          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5406          * @param {Array} el An array of elements to filter
5407          * @param {String} selector The simple selector to test
5408          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5409          * the selector instead of the ones that match
5410          * @return {Array}
5411          */
5412         filter : function(els, ss, nonMatches){
5413             ss = ss.replace(trimRe, "");
5414             if(!simpleCache[ss]){
5415                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5416             }
5417             var result = simpleCache[ss](els);
5418             return nonMatches ? quickDiff(result, els) : result;
5419         },
5420
5421         /**
5422          * Collection of matching regular expressions and code snippets.
5423          */
5424         matchers : [{
5425                 re: /^\.([\w-]+)/,
5426                 select: 'n = byClassName(n, null, " {1} ");'
5427             }, {
5428                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5429                 select: 'n = byPseudo(n, "{1}", "{2}");'
5430             },{
5431                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5432                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5433             }, {
5434                 re: /^#([\w-]+)/,
5435                 select: 'n = byId(n, null, "{1}");'
5436             },{
5437                 re: /^@([\w-]+)/,
5438                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5439             }
5440         ],
5441
5442         /**
5443          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5444          * 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;.
5445          */
5446         operators : {
5447             "=" : function(a, v){
5448                 return a == v;
5449             },
5450             "!=" : function(a, v){
5451                 return a != v;
5452             },
5453             "^=" : function(a, v){
5454                 return a && a.substr(0, v.length) == v;
5455             },
5456             "$=" : function(a, v){
5457                 return a && a.substr(a.length-v.length) == v;
5458             },
5459             "*=" : function(a, v){
5460                 return a && a.indexOf(v) !== -1;
5461             },
5462             "%=" : function(a, v){
5463                 return (a % v) == 0;
5464             },
5465             "|=" : function(a, v){
5466                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5467             },
5468             "~=" : function(a, v){
5469                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5470             }
5471         },
5472
5473         /**
5474          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5475          * and the argument (if any) supplied in the selector.
5476          */
5477         pseudos : {
5478             "first-child" : function(c){
5479                 var r = [], ri = -1, n;
5480                 for(var i = 0, ci; ci = n = c[i]; i++){
5481                     while((n = n.previousSibling) && n.nodeType != 1);
5482                     if(!n){
5483                         r[++ri] = ci;
5484                     }
5485                 }
5486                 return r;
5487             },
5488
5489             "last-child" : function(c){
5490                 var r = [], ri = -1, n;
5491                 for(var i = 0, ci; ci = n = c[i]; i++){
5492                     while((n = n.nextSibling) && n.nodeType != 1);
5493                     if(!n){
5494                         r[++ri] = ci;
5495                     }
5496                 }
5497                 return r;
5498             },
5499
5500             "nth-child" : function(c, a) {
5501                 var r = [], ri = -1;
5502                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5503                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5504                 for(var i = 0, n; n = c[i]; i++){
5505                     var pn = n.parentNode;
5506                     if (batch != pn._batch) {
5507                         var j = 0;
5508                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5509                             if(cn.nodeType == 1){
5510                                cn.nodeIndex = ++j;
5511                             }
5512                         }
5513                         pn._batch = batch;
5514                     }
5515                     if (f == 1) {
5516                         if (l == 0 || n.nodeIndex == l){
5517                             r[++ri] = n;
5518                         }
5519                     } else if ((n.nodeIndex + l) % f == 0){
5520                         r[++ri] = n;
5521                     }
5522                 }
5523
5524                 return r;
5525             },
5526
5527             "only-child" : function(c){
5528                 var r = [], ri = -1;;
5529                 for(var i = 0, ci; ci = c[i]; i++){
5530                     if(!prev(ci) && !next(ci)){
5531                         r[++ri] = ci;
5532                     }
5533                 }
5534                 return r;
5535             },
5536
5537             "empty" : function(c){
5538                 var r = [], ri = -1;
5539                 for(var i = 0, ci; ci = c[i]; i++){
5540                     var cns = ci.childNodes, j = 0, cn, empty = true;
5541                     while(cn = cns[j]){
5542                         ++j;
5543                         if(cn.nodeType == 1 || cn.nodeType == 3){
5544                             empty = false;
5545                             break;
5546                         }
5547                     }
5548                     if(empty){
5549                         r[++ri] = ci;
5550                     }
5551                 }
5552                 return r;
5553             },
5554
5555             "contains" : function(c, v){
5556                 var r = [], ri = -1;
5557                 for(var i = 0, ci; ci = c[i]; i++){
5558                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5559                         r[++ri] = ci;
5560                     }
5561                 }
5562                 return r;
5563             },
5564
5565             "nodeValue" : function(c, v){
5566                 var r = [], ri = -1;
5567                 for(var i = 0, ci; ci = c[i]; i++){
5568                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5569                         r[++ri] = ci;
5570                     }
5571                 }
5572                 return r;
5573             },
5574
5575             "checked" : function(c){
5576                 var r = [], ri = -1;
5577                 for(var i = 0, ci; ci = c[i]; i++){
5578                     if(ci.checked == true){
5579                         r[++ri] = ci;
5580                     }
5581                 }
5582                 return r;
5583             },
5584
5585             "not" : function(c, ss){
5586                 return Roo.DomQuery.filter(c, ss, true);
5587             },
5588
5589             "odd" : function(c){
5590                 return this["nth-child"](c, "odd");
5591             },
5592
5593             "even" : function(c){
5594                 return this["nth-child"](c, "even");
5595             },
5596
5597             "nth" : function(c, a){
5598                 return c[a-1] || [];
5599             },
5600
5601             "first" : function(c){
5602                 return c[0] || [];
5603             },
5604
5605             "last" : function(c){
5606                 return c[c.length-1] || [];
5607             },
5608
5609             "has" : function(c, ss){
5610                 var s = Roo.DomQuery.select;
5611                 var r = [], ri = -1;
5612                 for(var i = 0, ci; ci = c[i]; i++){
5613                     if(s(ss, ci).length > 0){
5614                         r[++ri] = ci;
5615                     }
5616                 }
5617                 return r;
5618             },
5619
5620             "next" : function(c, ss){
5621                 var is = Roo.DomQuery.is;
5622                 var r = [], ri = -1;
5623                 for(var i = 0, ci; ci = c[i]; i++){
5624                     var n = next(ci);
5625                     if(n && is(n, ss)){
5626                         r[++ri] = ci;
5627                     }
5628                 }
5629                 return r;
5630             },
5631
5632             "prev" : function(c, ss){
5633                 var is = Roo.DomQuery.is;
5634                 var r = [], ri = -1;
5635                 for(var i = 0, ci; ci = c[i]; i++){
5636                     var n = prev(ci);
5637                     if(n && is(n, ss)){
5638                         r[++ri] = ci;
5639                     }
5640                 }
5641                 return r;
5642             }
5643         }
5644     };
5645 }();
5646
5647 /**
5648  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5649  * @param {String} path The selector/xpath query
5650  * @param {Node} root (optional) The start of the query (defaults to document).
5651  * @return {Array}
5652  * @member Roo
5653  * @method query
5654  */
5655 Roo.query = Roo.DomQuery.select;
5656 /*
5657  * Based on:
5658  * Ext JS Library 1.1.1
5659  * Copyright(c) 2006-2007, Ext JS, LLC.
5660  *
5661  * Originally Released Under LGPL - original licence link has changed is not relivant.
5662  *
5663  * Fork - LGPL
5664  * <script type="text/javascript">
5665  */
5666
5667 /**
5668  * @class Roo.util.Observable
5669  * Base class that provides a common interface for publishing events. Subclasses are expected to
5670  * to have a property "events" with all the events defined.<br>
5671  * For example:
5672  * <pre><code>
5673  Employee = function(name){
5674     this.name = name;
5675     this.addEvents({
5676         "fired" : true,
5677         "quit" : true
5678     });
5679  }
5680  Roo.extend(Employee, Roo.util.Observable);
5681 </code></pre>
5682  * @param {Object} config properties to use (incuding events / listeners)
5683  */
5684
5685 Roo.util.Observable = function(cfg){
5686     
5687     cfg = cfg|| {};
5688     this.addEvents(cfg.events || {});
5689     if (cfg.events) {
5690         delete cfg.events; // make sure
5691     }
5692      
5693     Roo.apply(this, cfg);
5694     
5695     if(this.listeners){
5696         this.on(this.listeners);
5697         delete this.listeners;
5698     }
5699 };
5700 Roo.util.Observable.prototype = {
5701     /** 
5702  * @cfg {Object} listeners  list of events and functions to call for this object, 
5703  * For example :
5704  * <pre><code>
5705     listeners :  { 
5706        'click' : function(e) {
5707            ..... 
5708         } ,
5709         .... 
5710     } 
5711   </code></pre>
5712  */
5713     
5714     
5715     /**
5716      * Fires the specified event with the passed parameters (minus the event name).
5717      * @param {String} eventName
5718      * @param {Object...} args Variable number of parameters are passed to handlers
5719      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5720      */
5721     fireEvent : function(){
5722         var ce = this.events[arguments[0].toLowerCase()];
5723         if(typeof ce == "object"){
5724             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5725         }else{
5726             return true;
5727         }
5728     },
5729
5730     // private
5731     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5732
5733     /**
5734      * Appends an event handler to this component
5735      * @param {String}   eventName The type of event to listen for
5736      * @param {Function} handler The method the event invokes
5737      * @param {Object}   scope (optional) The scope in which to execute the handler
5738      * function. The handler function's "this" context.
5739      * @param {Object}   options (optional) An object containing handler configuration
5740      * properties. This may contain any of the following properties:<ul>
5741      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5742      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5743      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5744      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5745      * by the specified number of milliseconds. If the event fires again within that time, the original
5746      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5747      * </ul><br>
5748      * <p>
5749      * <b>Combining Options</b><br>
5750      * Using the options argument, it is possible to combine different types of listeners:<br>
5751      * <br>
5752      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5753                 <pre><code>
5754                 el.on('click', this.onClick, this, {
5755                         single: true,
5756                 delay: 100,
5757                 forumId: 4
5758                 });
5759                 </code></pre>
5760      * <p>
5761      * <b>Attaching multiple handlers in 1 call</b><br>
5762      * The method also allows for a single argument to be passed which is a config object containing properties
5763      * which specify multiple handlers.
5764      * <pre><code>
5765                 el.on({
5766                         'click': {
5767                         fn: this.onClick,
5768                         scope: this,
5769                         delay: 100
5770                 }, 
5771                 'mouseover': {
5772                         fn: this.onMouseOver,
5773                         scope: this
5774                 },
5775                 'mouseout': {
5776                         fn: this.onMouseOut,
5777                         scope: this
5778                 }
5779                 });
5780                 </code></pre>
5781      * <p>
5782      * Or a shorthand syntax which passes the same scope object to all handlers:
5783         <pre><code>
5784                 el.on({
5785                         'click': this.onClick,
5786                 'mouseover': this.onMouseOver,
5787                 'mouseout': this.onMouseOut,
5788                 scope: this
5789                 });
5790                 </code></pre>
5791      */
5792     addListener : function(eventName, fn, scope, o){
5793         if(typeof eventName == "object"){
5794             o = eventName;
5795             for(var e in o){
5796                 if(this.filterOptRe.test(e)){
5797                     continue;
5798                 }
5799                 if(typeof o[e] == "function"){
5800                     // shared options
5801                     this.addListener(e, o[e], o.scope,  o);
5802                 }else{
5803                     // individual options
5804                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5805                 }
5806             }
5807             return;
5808         }
5809         o = (!o || typeof o == "boolean") ? {} : o;
5810         eventName = eventName.toLowerCase();
5811         var ce = this.events[eventName] || true;
5812         if(typeof ce == "boolean"){
5813             ce = new Roo.util.Event(this, eventName);
5814             this.events[eventName] = ce;
5815         }
5816         ce.addListener(fn, scope, o);
5817     },
5818
5819     /**
5820      * Removes a listener
5821      * @param {String}   eventName     The type of event to listen for
5822      * @param {Function} handler        The handler to remove
5823      * @param {Object}   scope  (optional) The scope (this object) for the handler
5824      */
5825     removeListener : function(eventName, fn, scope){
5826         var ce = this.events[eventName.toLowerCase()];
5827         if(typeof ce == "object"){
5828             ce.removeListener(fn, scope);
5829         }
5830     },
5831
5832     /**
5833      * Removes all listeners for this object
5834      */
5835     purgeListeners : function(){
5836         for(var evt in this.events){
5837             if(typeof this.events[evt] == "object"){
5838                  this.events[evt].clearListeners();
5839             }
5840         }
5841     },
5842
5843     relayEvents : function(o, events){
5844         var createHandler = function(ename){
5845             return function(){
5846                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5847             };
5848         };
5849         for(var i = 0, len = events.length; i < len; i++){
5850             var ename = events[i];
5851             if(!this.events[ename]){ this.events[ename] = true; };
5852             o.on(ename, createHandler(ename), this);
5853         }
5854     },
5855
5856     /**
5857      * Used to define events on this Observable
5858      * @param {Object} object The object with the events defined
5859      */
5860     addEvents : function(o){
5861         if(!this.events){
5862             this.events = {};
5863         }
5864         Roo.applyIf(this.events, o);
5865     },
5866
5867     /**
5868      * Checks to see if this object has any listeners for a specified event
5869      * @param {String} eventName The name of the event to check for
5870      * @return {Boolean} True if the event is being listened for, else false
5871      */
5872     hasListener : function(eventName){
5873         var e = this.events[eventName];
5874         return typeof e == "object" && e.listeners.length > 0;
5875     }
5876 };
5877 /**
5878  * Appends an event handler to this element (shorthand for addListener)
5879  * @param {String}   eventName     The type of event to listen for
5880  * @param {Function} handler        The method the event invokes
5881  * @param {Object}   scope (optional) The scope in which to execute the handler
5882  * function. The handler function's "this" context.
5883  * @param {Object}   options  (optional)
5884  * @method
5885  */
5886 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5887 /**
5888  * Removes a listener (shorthand for removeListener)
5889  * @param {String}   eventName     The type of event to listen for
5890  * @param {Function} handler        The handler to remove
5891  * @param {Object}   scope  (optional) The scope (this object) for the handler
5892  * @method
5893  */
5894 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5895
5896 /**
5897  * Starts capture on the specified Observable. All events will be passed
5898  * to the supplied function with the event name + standard signature of the event
5899  * <b>before</b> the event is fired. If the supplied function returns false,
5900  * the event will not fire.
5901  * @param {Observable} o The Observable to capture
5902  * @param {Function} fn The function to call
5903  * @param {Object} scope (optional) The scope (this object) for the fn
5904  * @static
5905  */
5906 Roo.util.Observable.capture = function(o, fn, scope){
5907     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5908 };
5909
5910 /**
5911  * Removes <b>all</b> added captures from the Observable.
5912  * @param {Observable} o The Observable to release
5913  * @static
5914  */
5915 Roo.util.Observable.releaseCapture = function(o){
5916     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5917 };
5918
5919 (function(){
5920
5921     var createBuffered = function(h, o, scope){
5922         var task = new Roo.util.DelayedTask();
5923         return function(){
5924             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5925         };
5926     };
5927
5928     var createSingle = function(h, e, fn, scope){
5929         return function(){
5930             e.removeListener(fn, scope);
5931             return h.apply(scope, arguments);
5932         };
5933     };
5934
5935     var createDelayed = function(h, o, scope){
5936         return function(){
5937             var args = Array.prototype.slice.call(arguments, 0);
5938             setTimeout(function(){
5939                 h.apply(scope, args);
5940             }, o.delay || 10);
5941         };
5942     };
5943
5944     Roo.util.Event = function(obj, name){
5945         this.name = name;
5946         this.obj = obj;
5947         this.listeners = [];
5948     };
5949
5950     Roo.util.Event.prototype = {
5951         addListener : function(fn, scope, options){
5952             var o = options || {};
5953             scope = scope || this.obj;
5954             if(!this.isListening(fn, scope)){
5955                 var l = {fn: fn, scope: scope, options: o};
5956                 var h = fn;
5957                 if(o.delay){
5958                     h = createDelayed(h, o, scope);
5959                 }
5960                 if(o.single){
5961                     h = createSingle(h, this, fn, scope);
5962                 }
5963                 if(o.buffer){
5964                     h = createBuffered(h, o, scope);
5965                 }
5966                 l.fireFn = h;
5967                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5968                     this.listeners.push(l);
5969                 }else{
5970                     this.listeners = this.listeners.slice(0);
5971                     this.listeners.push(l);
5972                 }
5973             }
5974         },
5975
5976         findListener : function(fn, scope){
5977             scope = scope || this.obj;
5978             var ls = this.listeners;
5979             for(var i = 0, len = ls.length; i < len; i++){
5980                 var l = ls[i];
5981                 if(l.fn == fn && l.scope == scope){
5982                     return i;
5983                 }
5984             }
5985             return -1;
5986         },
5987
5988         isListening : function(fn, scope){
5989             return this.findListener(fn, scope) != -1;
5990         },
5991
5992         removeListener : function(fn, scope){
5993             var index;
5994             if((index = this.findListener(fn, scope)) != -1){
5995                 if(!this.firing){
5996                     this.listeners.splice(index, 1);
5997                 }else{
5998                     this.listeners = this.listeners.slice(0);
5999                     this.listeners.splice(index, 1);
6000                 }
6001                 return true;
6002             }
6003             return false;
6004         },
6005
6006         clearListeners : function(){
6007             this.listeners = [];
6008         },
6009
6010         fire : function(){
6011             var ls = this.listeners, scope, len = ls.length;
6012             if(len > 0){
6013                 this.firing = true;
6014                 var args = Array.prototype.slice.call(arguments, 0);
6015                 for(var i = 0; i < len; i++){
6016                     var l = ls[i];
6017                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6018                         this.firing = false;
6019                         return false;
6020                     }
6021                 }
6022                 this.firing = false;
6023             }
6024             return true;
6025         }
6026     };
6027 })();/*
6028  * Based on:
6029  * Ext JS Library 1.1.1
6030  * Copyright(c) 2006-2007, Ext JS, LLC.
6031  *
6032  * Originally Released Under LGPL - original licence link has changed is not relivant.
6033  *
6034  * Fork - LGPL
6035  * <script type="text/javascript">
6036  */
6037
6038 /**
6039  * @class Roo.EventManager
6040  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6041  * several useful events directly.
6042  * See {@link Roo.EventObject} for more details on normalized event objects.
6043  * @singleton
6044  */
6045 Roo.EventManager = function(){
6046     var docReadyEvent, docReadyProcId, docReadyState = false;
6047     var resizeEvent, resizeTask, textEvent, textSize;
6048     var E = Roo.lib.Event;
6049     var D = Roo.lib.Dom;
6050
6051     
6052     
6053
6054     var fireDocReady = function(){
6055         if(!docReadyState){
6056             docReadyState = true;
6057             Roo.isReady = true;
6058             if(docReadyProcId){
6059                 clearInterval(docReadyProcId);
6060             }
6061             if(Roo.isGecko || Roo.isOpera) {
6062                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6063             }
6064             if(Roo.isIE){
6065                 var defer = document.getElementById("ie-deferred-loader");
6066                 if(defer){
6067                     defer.onreadystatechange = null;
6068                     defer.parentNode.removeChild(defer);
6069                 }
6070             }
6071             if(docReadyEvent){
6072                 docReadyEvent.fire();
6073                 docReadyEvent.clearListeners();
6074             }
6075         }
6076     };
6077     
6078     var initDocReady = function(){
6079         docReadyEvent = new Roo.util.Event();
6080         if(Roo.isGecko || Roo.isOpera) {
6081             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6082         }else if(Roo.isIE){
6083             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6084             var defer = document.getElementById("ie-deferred-loader");
6085             defer.onreadystatechange = function(){
6086                 if(this.readyState == "complete"){
6087                     fireDocReady();
6088                 }
6089             };
6090         }else if(Roo.isSafari){ 
6091             docReadyProcId = setInterval(function(){
6092                 var rs = document.readyState;
6093                 if(rs == "complete") {
6094                     fireDocReady();     
6095                  }
6096             }, 10);
6097         }
6098         // no matter what, make sure it fires on load
6099         E.on(window, "load", fireDocReady);
6100     };
6101
6102     var createBuffered = function(h, o){
6103         var task = new Roo.util.DelayedTask(h);
6104         return function(e){
6105             // create new event object impl so new events don't wipe out properties
6106             e = new Roo.EventObjectImpl(e);
6107             task.delay(o.buffer, h, null, [e]);
6108         };
6109     };
6110
6111     var createSingle = function(h, el, ename, fn){
6112         return function(e){
6113             Roo.EventManager.removeListener(el, ename, fn);
6114             h(e);
6115         };
6116     };
6117
6118     var createDelayed = function(h, o){
6119         return function(e){
6120             // create new event object impl so new events don't wipe out properties
6121             e = new Roo.EventObjectImpl(e);
6122             setTimeout(function(){
6123                 h(e);
6124             }, o.delay || 10);
6125         };
6126     };
6127     var transitionEndVal = false;
6128     
6129     var transitionEnd = function()
6130     {
6131         if (transitionEndVal) {
6132             return transitionEndVal;
6133         }
6134         var el = document.createElement('div');
6135
6136         var transEndEventNames = {
6137             WebkitTransition : 'webkitTransitionEnd',
6138             MozTransition    : 'transitionend',
6139             OTransition      : 'oTransitionEnd otransitionend',
6140             transition       : 'transitionend'
6141         };
6142     
6143         for (var name in transEndEventNames) {
6144             if (el.style[name] !== undefined) {
6145                 transitionEndVal = transEndEventNames[name];
6146                 return  transitionEndVal ;
6147             }
6148         }
6149     }
6150     
6151
6152     var listen = function(element, ename, opt, fn, scope){
6153         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6154         fn = fn || o.fn; scope = scope || o.scope;
6155         var el = Roo.getDom(element);
6156         
6157         
6158         if(!el){
6159             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6160         }
6161         
6162         if (ename == 'transitionend') {
6163             ename = transitionEnd();
6164         }
6165         var h = function(e){
6166             e = Roo.EventObject.setEvent(e);
6167             var t;
6168             if(o.delegate){
6169                 t = e.getTarget(o.delegate, el);
6170                 if(!t){
6171                     return;
6172                 }
6173             }else{
6174                 t = e.target;
6175             }
6176             if(o.stopEvent === true){
6177                 e.stopEvent();
6178             }
6179             if(o.preventDefault === true){
6180                e.preventDefault();
6181             }
6182             if(o.stopPropagation === true){
6183                 e.stopPropagation();
6184             }
6185
6186             if(o.normalized === false){
6187                 e = e.browserEvent;
6188             }
6189
6190             fn.call(scope || el, e, t, o);
6191         };
6192         if(o.delay){
6193             h = createDelayed(h, o);
6194         }
6195         if(o.single){
6196             h = createSingle(h, el, ename, fn);
6197         }
6198         if(o.buffer){
6199             h = createBuffered(h, o);
6200         }
6201         fn._handlers = fn._handlers || [];
6202         
6203         
6204         fn._handlers.push([Roo.id(el), ename, h]);
6205         
6206         
6207          
6208         E.on(el, ename, h);
6209         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6210             el.addEventListener("DOMMouseScroll", h, false);
6211             E.on(window, 'unload', function(){
6212                 el.removeEventListener("DOMMouseScroll", h, false);
6213             });
6214         }
6215         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6216             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6217         }
6218         return h;
6219     };
6220
6221     var stopListening = function(el, ename, fn){
6222         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6223         if(hds){
6224             for(var i = 0, len = hds.length; i < len; i++){
6225                 var h = hds[i];
6226                 if(h[0] == id && h[1] == ename){
6227                     hd = h[2];
6228                     hds.splice(i, 1);
6229                     break;
6230                 }
6231             }
6232         }
6233         E.un(el, ename, hd);
6234         el = Roo.getDom(el);
6235         if(ename == "mousewheel" && el.addEventListener){
6236             el.removeEventListener("DOMMouseScroll", hd, false);
6237         }
6238         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6239             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6240         }
6241     };
6242
6243     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6244     
6245     var pub = {
6246         
6247         
6248         /** 
6249          * Fix for doc tools
6250          * @scope Roo.EventManager
6251          */
6252         
6253         
6254         /** 
6255          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6256          * object with a Roo.EventObject
6257          * @param {Function} fn        The method the event invokes
6258          * @param {Object}   scope    An object that becomes the scope of the handler
6259          * @param {boolean}  override If true, the obj passed in becomes
6260          *                             the execution scope of the listener
6261          * @return {Function} The wrapped function
6262          * @deprecated
6263          */
6264         wrap : function(fn, scope, override){
6265             return function(e){
6266                 Roo.EventObject.setEvent(e);
6267                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6268             };
6269         },
6270         
6271         /**
6272      * Appends an event handler to an element (shorthand for addListener)
6273      * @param {String/HTMLElement}   element        The html element or id to assign the
6274      * @param {String}   eventName The type of event to listen for
6275      * @param {Function} handler The method the event invokes
6276      * @param {Object}   scope (optional) The scope in which to execute the handler
6277      * function. The handler function's "this" context.
6278      * @param {Object}   options (optional) An object containing handler configuration
6279      * properties. This may contain any of the following properties:<ul>
6280      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6281      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6282      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6283      * <li>preventDefault {Boolean} True to prevent the default action</li>
6284      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6285      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6286      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6287      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6288      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6289      * by the specified number of milliseconds. If the event fires again within that time, the original
6290      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6291      * </ul><br>
6292      * <p>
6293      * <b>Combining Options</b><br>
6294      * Using the options argument, it is possible to combine different types of listeners:<br>
6295      * <br>
6296      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6297      * Code:<pre><code>
6298 el.on('click', this.onClick, this, {
6299     single: true,
6300     delay: 100,
6301     stopEvent : true,
6302     forumId: 4
6303 });</code></pre>
6304      * <p>
6305      * <b>Attaching multiple handlers in 1 call</b><br>
6306       * The method also allows for a single argument to be passed which is a config object containing properties
6307      * which specify multiple handlers.
6308      * <p>
6309      * Code:<pre><code>
6310 el.on({
6311     'click' : {
6312         fn: this.onClick
6313         scope: this,
6314         delay: 100
6315     },
6316     'mouseover' : {
6317         fn: this.onMouseOver
6318         scope: this
6319     },
6320     'mouseout' : {
6321         fn: this.onMouseOut
6322         scope: this
6323     }
6324 });</code></pre>
6325      * <p>
6326      * Or a shorthand syntax:<br>
6327      * Code:<pre><code>
6328 el.on({
6329     'click' : this.onClick,
6330     'mouseover' : this.onMouseOver,
6331     'mouseout' : this.onMouseOut
6332     scope: this
6333 });</code></pre>
6334      */
6335         addListener : function(element, eventName, fn, scope, options){
6336             if(typeof eventName == "object"){
6337                 var o = eventName;
6338                 for(var e in o){
6339                     if(propRe.test(e)){
6340                         continue;
6341                     }
6342                     if(typeof o[e] == "function"){
6343                         // shared options
6344                         listen(element, e, o, o[e], o.scope);
6345                     }else{
6346                         // individual options
6347                         listen(element, e, o[e]);
6348                     }
6349                 }
6350                 return;
6351             }
6352             return listen(element, eventName, options, fn, scope);
6353         },
6354         
6355         /**
6356          * Removes an event handler
6357          *
6358          * @param {String/HTMLElement}   element        The id or html element to remove the 
6359          *                             event from
6360          * @param {String}   eventName     The type of event
6361          * @param {Function} fn
6362          * @return {Boolean} True if a listener was actually removed
6363          */
6364         removeListener : function(element, eventName, fn){
6365             return stopListening(element, eventName, fn);
6366         },
6367         
6368         /**
6369          * Fires when the document is ready (before onload and before images are loaded). Can be 
6370          * accessed shorthanded Roo.onReady().
6371          * @param {Function} fn        The method the event invokes
6372          * @param {Object}   scope    An  object that becomes the scope of the handler
6373          * @param {boolean}  options
6374          */
6375         onDocumentReady : function(fn, scope, options){
6376             if(docReadyState){ // if it already fired
6377                 docReadyEvent.addListener(fn, scope, options);
6378                 docReadyEvent.fire();
6379                 docReadyEvent.clearListeners();
6380                 return;
6381             }
6382             if(!docReadyEvent){
6383                 initDocReady();
6384             }
6385             docReadyEvent.addListener(fn, scope, options);
6386         },
6387         
6388         /**
6389          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6390          * @param {Function} fn        The method the event invokes
6391          * @param {Object}   scope    An object that becomes the scope of the handler
6392          * @param {boolean}  options
6393          */
6394         onWindowResize : function(fn, scope, options){
6395             if(!resizeEvent){
6396                 resizeEvent = new Roo.util.Event();
6397                 resizeTask = new Roo.util.DelayedTask(function(){
6398                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6399                 });
6400                 E.on(window, "resize", function(){
6401                     if(Roo.isIE){
6402                         resizeTask.delay(50);
6403                     }else{
6404                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6405                     }
6406                 });
6407             }
6408             resizeEvent.addListener(fn, scope, options);
6409         },
6410
6411         /**
6412          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6413          * @param {Function} fn        The method the event invokes
6414          * @param {Object}   scope    An object that becomes the scope of the handler
6415          * @param {boolean}  options
6416          */
6417         onTextResize : function(fn, scope, options){
6418             if(!textEvent){
6419                 textEvent = new Roo.util.Event();
6420                 var textEl = new Roo.Element(document.createElement('div'));
6421                 textEl.dom.className = 'x-text-resize';
6422                 textEl.dom.innerHTML = 'X';
6423                 textEl.appendTo(document.body);
6424                 textSize = textEl.dom.offsetHeight;
6425                 setInterval(function(){
6426                     if(textEl.dom.offsetHeight != textSize){
6427                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6428                     }
6429                 }, this.textResizeInterval);
6430             }
6431             textEvent.addListener(fn, scope, options);
6432         },
6433
6434         /**
6435          * Removes the passed window resize listener.
6436          * @param {Function} fn        The method the event invokes
6437          * @param {Object}   scope    The scope of handler
6438          */
6439         removeResizeListener : function(fn, scope){
6440             if(resizeEvent){
6441                 resizeEvent.removeListener(fn, scope);
6442             }
6443         },
6444
6445         // private
6446         fireResize : function(){
6447             if(resizeEvent){
6448                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6449             }   
6450         },
6451         /**
6452          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6453          */
6454         ieDeferSrc : false,
6455         /**
6456          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6457          */
6458         textResizeInterval : 50
6459     };
6460     
6461     /**
6462      * Fix for doc tools
6463      * @scopeAlias pub=Roo.EventManager
6464      */
6465     
6466      /**
6467      * Appends an event handler to an element (shorthand for addListener)
6468      * @param {String/HTMLElement}   element        The html element or id to assign the
6469      * @param {String}   eventName The type of event to listen for
6470      * @param {Function} handler The method the event invokes
6471      * @param {Object}   scope (optional) The scope in which to execute the handler
6472      * function. The handler function's "this" context.
6473      * @param {Object}   options (optional) An object containing handler configuration
6474      * properties. This may contain any of the following properties:<ul>
6475      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6476      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6477      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6478      * <li>preventDefault {Boolean} True to prevent the default action</li>
6479      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6480      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6481      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6482      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6483      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6484      * by the specified number of milliseconds. If the event fires again within that time, the original
6485      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6486      * </ul><br>
6487      * <p>
6488      * <b>Combining Options</b><br>
6489      * Using the options argument, it is possible to combine different types of listeners:<br>
6490      * <br>
6491      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6492      * Code:<pre><code>
6493 el.on('click', this.onClick, this, {
6494     single: true,
6495     delay: 100,
6496     stopEvent : true,
6497     forumId: 4
6498 });</code></pre>
6499      * <p>
6500      * <b>Attaching multiple handlers in 1 call</b><br>
6501       * The method also allows for a single argument to be passed which is a config object containing properties
6502      * which specify multiple handlers.
6503      * <p>
6504      * Code:<pre><code>
6505 el.on({
6506     'click' : {
6507         fn: this.onClick
6508         scope: this,
6509         delay: 100
6510     },
6511     'mouseover' : {
6512         fn: this.onMouseOver
6513         scope: this
6514     },
6515     'mouseout' : {
6516         fn: this.onMouseOut
6517         scope: this
6518     }
6519 });</code></pre>
6520      * <p>
6521      * Or a shorthand syntax:<br>
6522      * Code:<pre><code>
6523 el.on({
6524     'click' : this.onClick,
6525     'mouseover' : this.onMouseOver,
6526     'mouseout' : this.onMouseOut
6527     scope: this
6528 });</code></pre>
6529      */
6530     pub.on = pub.addListener;
6531     pub.un = pub.removeListener;
6532
6533     pub.stoppedMouseDownEvent = new Roo.util.Event();
6534     return pub;
6535 }();
6536 /**
6537   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6538   * @param {Function} fn        The method the event invokes
6539   * @param {Object}   scope    An  object that becomes the scope of the handler
6540   * @param {boolean}  override If true, the obj passed in becomes
6541   *                             the execution scope of the listener
6542   * @member Roo
6543   * @method onReady
6544  */
6545 Roo.onReady = Roo.EventManager.onDocumentReady;
6546
6547 Roo.onReady(function(){
6548     var bd = Roo.get(document.body);
6549     if(!bd){ return; }
6550
6551     var cls = [
6552             Roo.isIE ? "roo-ie"
6553             : Roo.isGecko ? "roo-gecko"
6554             : Roo.isOpera ? "roo-opera"
6555             : Roo.isSafari ? "roo-safari" : ""];
6556
6557     if(Roo.isMac){
6558         cls.push("roo-mac");
6559     }
6560     if(Roo.isLinux){
6561         cls.push("roo-linux");
6562     }
6563     if(Roo.isBorderBox){
6564         cls.push('roo-border-box');
6565     }
6566     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6567         var p = bd.dom.parentNode;
6568         if(p){
6569             p.className += ' roo-strict';
6570         }
6571     }
6572     bd.addClass(cls.join(' '));
6573 });
6574
6575 /**
6576  * @class Roo.EventObject
6577  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6578  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6579  * Example:
6580  * <pre><code>
6581  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6582     e.preventDefault();
6583     var target = e.getTarget();
6584     ...
6585  }
6586  var myDiv = Roo.get("myDiv");
6587  myDiv.on("click", handleClick);
6588  //or
6589  Roo.EventManager.on("myDiv", 'click', handleClick);
6590  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6591  </code></pre>
6592  * @singleton
6593  */
6594 Roo.EventObject = function(){
6595     
6596     var E = Roo.lib.Event;
6597     
6598     // safari keypress events for special keys return bad keycodes
6599     var safariKeys = {
6600         63234 : 37, // left
6601         63235 : 39, // right
6602         63232 : 38, // up
6603         63233 : 40, // down
6604         63276 : 33, // page up
6605         63277 : 34, // page down
6606         63272 : 46, // delete
6607         63273 : 36, // home
6608         63275 : 35  // end
6609     };
6610
6611     // normalize button clicks
6612     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6613                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6614
6615     Roo.EventObjectImpl = function(e){
6616         if(e){
6617             this.setEvent(e.browserEvent || e);
6618         }
6619     };
6620     Roo.EventObjectImpl.prototype = {
6621         /**
6622          * Used to fix doc tools.
6623          * @scope Roo.EventObject.prototype
6624          */
6625             
6626
6627         
6628         
6629         /** The normal browser event */
6630         browserEvent : null,
6631         /** The button pressed in a mouse event */
6632         button : -1,
6633         /** True if the shift key was down during the event */
6634         shiftKey : false,
6635         /** True if the control key was down during the event */
6636         ctrlKey : false,
6637         /** True if the alt key was down during the event */
6638         altKey : false,
6639
6640         /** Key constant 
6641         * @type Number */
6642         BACKSPACE : 8,
6643         /** Key constant 
6644         * @type Number */
6645         TAB : 9,
6646         /** Key constant 
6647         * @type Number */
6648         RETURN : 13,
6649         /** Key constant 
6650         * @type Number */
6651         ENTER : 13,
6652         /** Key constant 
6653         * @type Number */
6654         SHIFT : 16,
6655         /** Key constant 
6656         * @type Number */
6657         CONTROL : 17,
6658         /** Key constant 
6659         * @type Number */
6660         ESC : 27,
6661         /** Key constant 
6662         * @type Number */
6663         SPACE : 32,
6664         /** Key constant 
6665         * @type Number */
6666         PAGEUP : 33,
6667         /** Key constant 
6668         * @type Number */
6669         PAGEDOWN : 34,
6670         /** Key constant 
6671         * @type Number */
6672         END : 35,
6673         /** Key constant 
6674         * @type Number */
6675         HOME : 36,
6676         /** Key constant 
6677         * @type Number */
6678         LEFT : 37,
6679         /** Key constant 
6680         * @type Number */
6681         UP : 38,
6682         /** Key constant 
6683         * @type Number */
6684         RIGHT : 39,
6685         /** Key constant 
6686         * @type Number */
6687         DOWN : 40,
6688         /** Key constant 
6689         * @type Number */
6690         DELETE : 46,
6691         /** Key constant 
6692         * @type Number */
6693         F5 : 116,
6694
6695            /** @private */
6696         setEvent : function(e){
6697             if(e == this || (e && e.browserEvent)){ // already wrapped
6698                 return e;
6699             }
6700             this.browserEvent = e;
6701             if(e){
6702                 // normalize buttons
6703                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6704                 if(e.type == 'click' && this.button == -1){
6705                     this.button = 0;
6706                 }
6707                 this.type = e.type;
6708                 this.shiftKey = e.shiftKey;
6709                 // mac metaKey behaves like ctrlKey
6710                 this.ctrlKey = e.ctrlKey || e.metaKey;
6711                 this.altKey = e.altKey;
6712                 // in getKey these will be normalized for the mac
6713                 this.keyCode = e.keyCode;
6714                 // keyup warnings on firefox.
6715                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6716                 // cache the target for the delayed and or buffered events
6717                 this.target = E.getTarget(e);
6718                 // same for XY
6719                 this.xy = E.getXY(e);
6720             }else{
6721                 this.button = -1;
6722                 this.shiftKey = false;
6723                 this.ctrlKey = false;
6724                 this.altKey = false;
6725                 this.keyCode = 0;
6726                 this.charCode =0;
6727                 this.target = null;
6728                 this.xy = [0, 0];
6729             }
6730             return this;
6731         },
6732
6733         /**
6734          * Stop the event (preventDefault and stopPropagation)
6735          */
6736         stopEvent : function(){
6737             if(this.browserEvent){
6738                 if(this.browserEvent.type == 'mousedown'){
6739                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6740                 }
6741                 E.stopEvent(this.browserEvent);
6742             }
6743         },
6744
6745         /**
6746          * Prevents the browsers default handling of the event.
6747          */
6748         preventDefault : function(){
6749             if(this.browserEvent){
6750                 E.preventDefault(this.browserEvent);
6751             }
6752         },
6753
6754         /** @private */
6755         isNavKeyPress : function(){
6756             var k = this.keyCode;
6757             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6758             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6759         },
6760
6761         isSpecialKey : function(){
6762             var k = this.keyCode;
6763             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6764             (k == 16) || (k == 17) ||
6765             (k >= 18 && k <= 20) ||
6766             (k >= 33 && k <= 35) ||
6767             (k >= 36 && k <= 39) ||
6768             (k >= 44 && k <= 45);
6769         },
6770         /**
6771          * Cancels bubbling of the event.
6772          */
6773         stopPropagation : function(){
6774             if(this.browserEvent){
6775                 if(this.type == 'mousedown'){
6776                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6777                 }
6778                 E.stopPropagation(this.browserEvent);
6779             }
6780         },
6781
6782         /**
6783          * Gets the key code for the event.
6784          * @return {Number}
6785          */
6786         getCharCode : function(){
6787             return this.charCode || this.keyCode;
6788         },
6789
6790         /**
6791          * Returns a normalized keyCode for the event.
6792          * @return {Number} The key code
6793          */
6794         getKey : function(){
6795             var k = this.keyCode || this.charCode;
6796             return Roo.isSafari ? (safariKeys[k] || k) : k;
6797         },
6798
6799         /**
6800          * Gets the x coordinate of the event.
6801          * @return {Number}
6802          */
6803         getPageX : function(){
6804             return this.xy[0];
6805         },
6806
6807         /**
6808          * Gets the y coordinate of the event.
6809          * @return {Number}
6810          */
6811         getPageY : function(){
6812             return this.xy[1];
6813         },
6814
6815         /**
6816          * Gets the time of the event.
6817          * @return {Number}
6818          */
6819         getTime : function(){
6820             if(this.browserEvent){
6821                 return E.getTime(this.browserEvent);
6822             }
6823             return null;
6824         },
6825
6826         /**
6827          * Gets the page coordinates of the event.
6828          * @return {Array} The xy values like [x, y]
6829          */
6830         getXY : function(){
6831             return this.xy;
6832         },
6833
6834         /**
6835          * Gets the target for the event.
6836          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6837          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6838                 search as a number or element (defaults to 10 || document.body)
6839          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6840          * @return {HTMLelement}
6841          */
6842         getTarget : function(selector, maxDepth, returnEl){
6843             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6844         },
6845         /**
6846          * Gets the related target.
6847          * @return {HTMLElement}
6848          */
6849         getRelatedTarget : function(){
6850             if(this.browserEvent){
6851                 return E.getRelatedTarget(this.browserEvent);
6852             }
6853             return null;
6854         },
6855
6856         /**
6857          * Normalizes mouse wheel delta across browsers
6858          * @return {Number} The delta
6859          */
6860         getWheelDelta : function(){
6861             var e = this.browserEvent;
6862             var delta = 0;
6863             if(e.wheelDelta){ /* IE/Opera. */
6864                 delta = e.wheelDelta/120;
6865             }else if(e.detail){ /* Mozilla case. */
6866                 delta = -e.detail/3;
6867             }
6868             return delta;
6869         },
6870
6871         /**
6872          * Returns true if the control, meta, shift or alt key was pressed during this event.
6873          * @return {Boolean}
6874          */
6875         hasModifier : function(){
6876             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6877         },
6878
6879         /**
6880          * Returns true if the target of this event equals el or is a child of el
6881          * @param {String/HTMLElement/Element} el
6882          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6883          * @return {Boolean}
6884          */
6885         within : function(el, related){
6886             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6887             return t && Roo.fly(el).contains(t);
6888         },
6889
6890         getPoint : function(){
6891             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6892         }
6893     };
6894
6895     return new Roo.EventObjectImpl();
6896 }();
6897             
6898     /*
6899  * Based on:
6900  * Ext JS Library 1.1.1
6901  * Copyright(c) 2006-2007, Ext JS, LLC.
6902  *
6903  * Originally Released Under LGPL - original licence link has changed is not relivant.
6904  *
6905  * Fork - LGPL
6906  * <script type="text/javascript">
6907  */
6908
6909  
6910 // was in Composite Element!??!?!
6911  
6912 (function(){
6913     var D = Roo.lib.Dom;
6914     var E = Roo.lib.Event;
6915     var A = Roo.lib.Anim;
6916
6917     // local style camelizing for speed
6918     var propCache = {};
6919     var camelRe = /(-[a-z])/gi;
6920     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6921     var view = document.defaultView;
6922
6923 /**
6924  * @class Roo.Element
6925  * Represents an Element in the DOM.<br><br>
6926  * Usage:<br>
6927 <pre><code>
6928 var el = Roo.get("my-div");
6929
6930 // or with getEl
6931 var el = getEl("my-div");
6932
6933 // or with a DOM element
6934 var el = Roo.get(myDivElement);
6935 </code></pre>
6936  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6937  * each call instead of constructing a new one.<br><br>
6938  * <b>Animations</b><br />
6939  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6940  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6941 <pre>
6942 Option    Default   Description
6943 --------- --------  ---------------------------------------------
6944 duration  .35       The duration of the animation in seconds
6945 easing    easeOut   The YUI easing method
6946 callback  none      A function to execute when the anim completes
6947 scope     this      The scope (this) of the callback function
6948 </pre>
6949 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6950 * manipulate the animation. Here's an example:
6951 <pre><code>
6952 var el = Roo.get("my-div");
6953
6954 // no animation
6955 el.setWidth(100);
6956
6957 // default animation
6958 el.setWidth(100, true);
6959
6960 // animation with some options set
6961 el.setWidth(100, {
6962     duration: 1,
6963     callback: this.foo,
6964     scope: this
6965 });
6966
6967 // using the "anim" property to get the Anim object
6968 var opt = {
6969     duration: 1,
6970     callback: this.foo,
6971     scope: this
6972 };
6973 el.setWidth(100, opt);
6974 ...
6975 if(opt.anim.isAnimated()){
6976     opt.anim.stop();
6977 }
6978 </code></pre>
6979 * <b> Composite (Collections of) Elements</b><br />
6980  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6981  * @constructor Create a new Element directly.
6982  * @param {String/HTMLElement} element
6983  * @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).
6984  */
6985     Roo.Element = function(element, forceNew){
6986         var dom = typeof element == "string" ?
6987                 document.getElementById(element) : element;
6988         if(!dom){ // invalid id/element
6989             return null;
6990         }
6991         var id = dom.id;
6992         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6993             return Roo.Element.cache[id];
6994         }
6995
6996         /**
6997          * The DOM element
6998          * @type HTMLElement
6999          */
7000         this.dom = dom;
7001
7002         /**
7003          * The DOM element ID
7004          * @type String
7005          */
7006         this.id = id || Roo.id(dom);
7007     };
7008
7009     var El = Roo.Element;
7010
7011     El.prototype = {
7012         /**
7013          * The element's default display mode  (defaults to "")
7014          * @type String
7015          */
7016         originalDisplay : "",
7017
7018         visibilityMode : 1,
7019         /**
7020          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7021          * @type String
7022          */
7023         defaultUnit : "px",
7024         /**
7025          * Sets the element's visibility mode. When setVisible() is called it
7026          * will use this to determine whether to set the visibility or the display property.
7027          * @param visMode Element.VISIBILITY or Element.DISPLAY
7028          * @return {Roo.Element} this
7029          */
7030         setVisibilityMode : function(visMode){
7031             this.visibilityMode = visMode;
7032             return this;
7033         },
7034         /**
7035          * Convenience method for setVisibilityMode(Element.DISPLAY)
7036          * @param {String} display (optional) What to set display to when visible
7037          * @return {Roo.Element} this
7038          */
7039         enableDisplayMode : function(display){
7040             this.setVisibilityMode(El.DISPLAY);
7041             if(typeof display != "undefined") this.originalDisplay = display;
7042             return this;
7043         },
7044
7045         /**
7046          * 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)
7047          * @param {String} selector The simple selector to test
7048          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7049                 search as a number or element (defaults to 10 || document.body)
7050          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7051          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7052          */
7053         findParent : function(simpleSelector, maxDepth, returnEl){
7054             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7055             maxDepth = maxDepth || 50;
7056             if(typeof maxDepth != "number"){
7057                 stopEl = Roo.getDom(maxDepth);
7058                 maxDepth = 10;
7059             }
7060             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7061                 if(dq.is(p, simpleSelector)){
7062                     return returnEl ? Roo.get(p) : p;
7063                 }
7064                 depth++;
7065                 p = p.parentNode;
7066             }
7067             return null;
7068         },
7069
7070
7071         /**
7072          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7073          * @param {String} selector The simple selector to test
7074          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7075                 search as a number or element (defaults to 10 || document.body)
7076          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7077          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7078          */
7079         findParentNode : function(simpleSelector, maxDepth, returnEl){
7080             var p = Roo.fly(this.dom.parentNode, '_internal');
7081             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7082         },
7083
7084         /**
7085          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7086          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7087          * @param {String} selector The simple selector to test
7088          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7089                 search as a number or element (defaults to 10 || document.body)
7090          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7091          */
7092         up : function(simpleSelector, maxDepth){
7093             return this.findParentNode(simpleSelector, maxDepth, true);
7094         },
7095
7096
7097
7098         /**
7099          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7100          * @param {String} selector The simple selector to test
7101          * @return {Boolean} True if this element matches the selector, else false
7102          */
7103         is : function(simpleSelector){
7104             return Roo.DomQuery.is(this.dom, simpleSelector);
7105         },
7106
7107         /**
7108          * Perform animation on this element.
7109          * @param {Object} args The YUI animation control args
7110          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7111          * @param {Function} onComplete (optional) Function to call when animation completes
7112          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7113          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7114          * @return {Roo.Element} this
7115          */
7116         animate : function(args, duration, onComplete, easing, animType){
7117             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7118             return this;
7119         },
7120
7121         /*
7122          * @private Internal animation call
7123          */
7124         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7125             animType = animType || 'run';
7126             opt = opt || {};
7127             var anim = Roo.lib.Anim[animType](
7128                 this.dom, args,
7129                 (opt.duration || defaultDur) || .35,
7130                 (opt.easing || defaultEase) || 'easeOut',
7131                 function(){
7132                     Roo.callback(cb, this);
7133                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7134                 },
7135                 this
7136             );
7137             opt.anim = anim;
7138             return anim;
7139         },
7140
7141         // private legacy anim prep
7142         preanim : function(a, i){
7143             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7144         },
7145
7146         /**
7147          * Removes worthless text nodes
7148          * @param {Boolean} forceReclean (optional) By default the element
7149          * keeps track if it has been cleaned already so
7150          * you can call this over and over. However, if you update the element and
7151          * need to force a reclean, you can pass true.
7152          */
7153         clean : function(forceReclean){
7154             if(this.isCleaned && forceReclean !== true){
7155                 return this;
7156             }
7157             var ns = /\S/;
7158             var d = this.dom, n = d.firstChild, ni = -1;
7159             while(n){
7160                 var nx = n.nextSibling;
7161                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7162                     d.removeChild(n);
7163                 }else{
7164                     n.nodeIndex = ++ni;
7165                 }
7166                 n = nx;
7167             }
7168             this.isCleaned = true;
7169             return this;
7170         },
7171
7172         // private
7173         calcOffsetsTo : function(el){
7174             el = Roo.get(el);
7175             var d = el.dom;
7176             var restorePos = false;
7177             if(el.getStyle('position') == 'static'){
7178                 el.position('relative');
7179                 restorePos = true;
7180             }
7181             var x = 0, y =0;
7182             var op = this.dom;
7183             while(op && op != d && op.tagName != 'HTML'){
7184                 x+= op.offsetLeft;
7185                 y+= op.offsetTop;
7186                 op = op.offsetParent;
7187             }
7188             if(restorePos){
7189                 el.position('static');
7190             }
7191             return [x, y];
7192         },
7193
7194         /**
7195          * Scrolls this element into view within the passed container.
7196          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7197          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7198          * @return {Roo.Element} this
7199          */
7200         scrollIntoView : function(container, hscroll){
7201             var c = Roo.getDom(container) || document.body;
7202             var el = this.dom;
7203
7204             var o = this.calcOffsetsTo(c),
7205                 l = o[0],
7206                 t = o[1],
7207                 b = t+el.offsetHeight,
7208                 r = l+el.offsetWidth;
7209
7210             var ch = c.clientHeight;
7211             var ct = parseInt(c.scrollTop, 10);
7212             var cl = parseInt(c.scrollLeft, 10);
7213             var cb = ct + ch;
7214             var cr = cl + c.clientWidth;
7215
7216             if(t < ct){
7217                 c.scrollTop = t;
7218             }else if(b > cb){
7219                 c.scrollTop = b-ch;
7220             }
7221
7222             if(hscroll !== false){
7223                 if(l < cl){
7224                     c.scrollLeft = l;
7225                 }else if(r > cr){
7226                     c.scrollLeft = r-c.clientWidth;
7227                 }
7228             }
7229             return this;
7230         },
7231
7232         // private
7233         scrollChildIntoView : function(child, hscroll){
7234             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7235         },
7236
7237         /**
7238          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7239          * the new height may not be available immediately.
7240          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7241          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7242          * @param {Function} onComplete (optional) Function to call when animation completes
7243          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7244          * @return {Roo.Element} this
7245          */
7246         autoHeight : function(animate, duration, onComplete, easing){
7247             var oldHeight = this.getHeight();
7248             this.clip();
7249             this.setHeight(1); // force clipping
7250             setTimeout(function(){
7251                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7252                 if(!animate){
7253                     this.setHeight(height);
7254                     this.unclip();
7255                     if(typeof onComplete == "function"){
7256                         onComplete();
7257                     }
7258                 }else{
7259                     this.setHeight(oldHeight); // restore original height
7260                     this.setHeight(height, animate, duration, function(){
7261                         this.unclip();
7262                         if(typeof onComplete == "function") onComplete();
7263                     }.createDelegate(this), easing);
7264                 }
7265             }.createDelegate(this), 0);
7266             return this;
7267         },
7268
7269         /**
7270          * Returns true if this element is an ancestor of the passed element
7271          * @param {HTMLElement/String} el The element to check
7272          * @return {Boolean} True if this element is an ancestor of el, else false
7273          */
7274         contains : function(el){
7275             if(!el){return false;}
7276             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7277         },
7278
7279         /**
7280          * Checks whether the element is currently visible using both visibility and display properties.
7281          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7282          * @return {Boolean} True if the element is currently visible, else false
7283          */
7284         isVisible : function(deep) {
7285             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7286             if(deep !== true || !vis){
7287                 return vis;
7288             }
7289             var p = this.dom.parentNode;
7290             while(p && p.tagName.toLowerCase() != "body"){
7291                 if(!Roo.fly(p, '_isVisible').isVisible()){
7292                     return false;
7293                 }
7294                 p = p.parentNode;
7295             }
7296             return true;
7297         },
7298
7299         /**
7300          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7301          * @param {String} selector The CSS selector
7302          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7303          * @return {CompositeElement/CompositeElementLite} The composite element
7304          */
7305         select : function(selector, unique){
7306             return El.select(selector, unique, this.dom);
7307         },
7308
7309         /**
7310          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7311          * @param {String} selector The CSS selector
7312          * @return {Array} An array of the matched nodes
7313          */
7314         query : function(selector, unique){
7315             return Roo.DomQuery.select(selector, this.dom);
7316         },
7317
7318         /**
7319          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7320          * @param {String} selector The CSS selector
7321          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7322          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7323          */
7324         child : function(selector, returnDom){
7325             var n = Roo.DomQuery.selectNode(selector, this.dom);
7326             return returnDom ? n : Roo.get(n);
7327         },
7328
7329         /**
7330          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7331          * @param {String} selector The CSS selector
7332          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7333          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7334          */
7335         down : function(selector, returnDom){
7336             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7337             return returnDom ? n : Roo.get(n);
7338         },
7339
7340         /**
7341          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7342          * @param {String} group The group the DD object is member of
7343          * @param {Object} config The DD config object
7344          * @param {Object} overrides An object containing methods to override/implement on the DD object
7345          * @return {Roo.dd.DD} The DD object
7346          */
7347         initDD : function(group, config, overrides){
7348             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7349             return Roo.apply(dd, overrides);
7350         },
7351
7352         /**
7353          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7354          * @param {String} group The group the DDProxy object is member of
7355          * @param {Object} config The DDProxy config object
7356          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7357          * @return {Roo.dd.DDProxy} The DDProxy object
7358          */
7359         initDDProxy : function(group, config, overrides){
7360             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7361             return Roo.apply(dd, overrides);
7362         },
7363
7364         /**
7365          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7366          * @param {String} group The group the DDTarget object is member of
7367          * @param {Object} config The DDTarget config object
7368          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7369          * @return {Roo.dd.DDTarget} The DDTarget object
7370          */
7371         initDDTarget : function(group, config, overrides){
7372             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7373             return Roo.apply(dd, overrides);
7374         },
7375
7376         /**
7377          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7378          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7379          * @param {Boolean} visible Whether the element is visible
7380          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7381          * @return {Roo.Element} this
7382          */
7383          setVisible : function(visible, animate){
7384             if(!animate || !A){
7385                 if(this.visibilityMode == El.DISPLAY){
7386                     this.setDisplayed(visible);
7387                 }else{
7388                     this.fixDisplay();
7389                     this.dom.style.visibility = visible ? "visible" : "hidden";
7390                 }
7391             }else{
7392                 // closure for composites
7393                 var dom = this.dom;
7394                 var visMode = this.visibilityMode;
7395                 if(visible){
7396                     this.setOpacity(.01);
7397                     this.setVisible(true);
7398                 }
7399                 this.anim({opacity: { to: (visible?1:0) }},
7400                       this.preanim(arguments, 1),
7401                       null, .35, 'easeIn', function(){
7402                          if(!visible){
7403                              if(visMode == El.DISPLAY){
7404                                  dom.style.display = "none";
7405                              }else{
7406                                  dom.style.visibility = "hidden";
7407                              }
7408                              Roo.get(dom).setOpacity(1);
7409                          }
7410                      });
7411             }
7412             return this;
7413         },
7414
7415         /**
7416          * Returns true if display is not "none"
7417          * @return {Boolean}
7418          */
7419         isDisplayed : function() {
7420             return this.getStyle("display") != "none";
7421         },
7422
7423         /**
7424          * Toggles the element's visibility or display, depending on visibility mode.
7425          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7426          * @return {Roo.Element} this
7427          */
7428         toggle : function(animate){
7429             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7430             return this;
7431         },
7432
7433         /**
7434          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7435          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7436          * @return {Roo.Element} this
7437          */
7438         setDisplayed : function(value) {
7439             if(typeof value == "boolean"){
7440                value = value ? this.originalDisplay : "none";
7441             }
7442             this.setStyle("display", value);
7443             return this;
7444         },
7445
7446         /**
7447          * Tries to focus the element. Any exceptions are caught and ignored.
7448          * @return {Roo.Element} this
7449          */
7450         focus : function() {
7451             try{
7452                 this.dom.focus();
7453             }catch(e){}
7454             return this;
7455         },
7456
7457         /**
7458          * Tries to blur the element. Any exceptions are caught and ignored.
7459          * @return {Roo.Element} this
7460          */
7461         blur : function() {
7462             try{
7463                 this.dom.blur();
7464             }catch(e){}
7465             return this;
7466         },
7467
7468         /**
7469          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7470          * @param {String/Array} className The CSS class to add, or an array of classes
7471          * @return {Roo.Element} this
7472          */
7473         addClass : function(className){
7474             if(className instanceof Array){
7475                 for(var i = 0, len = className.length; i < len; i++) {
7476                     this.addClass(className[i]);
7477                 }
7478             }else{
7479                 if(className && !this.hasClass(className)){
7480                     this.dom.className = this.dom.className + " " + className;
7481                 }
7482             }
7483             return this;
7484         },
7485
7486         /**
7487          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7488          * @param {String/Array} className The CSS class to add, or an array of classes
7489          * @return {Roo.Element} this
7490          */
7491         radioClass : function(className){
7492             var siblings = this.dom.parentNode.childNodes;
7493             for(var i = 0; i < siblings.length; i++) {
7494                 var s = siblings[i];
7495                 if(s.nodeType == 1){
7496                     Roo.get(s).removeClass(className);
7497                 }
7498             }
7499             this.addClass(className);
7500             return this;
7501         },
7502
7503         /**
7504          * Removes one or more CSS classes from the element.
7505          * @param {String/Array} className The CSS class to remove, or an array of classes
7506          * @return {Roo.Element} this
7507          */
7508         removeClass : function(className){
7509             if(!className || !this.dom.className){
7510                 return this;
7511             }
7512             if(className instanceof Array){
7513                 for(var i = 0, len = className.length; i < len; i++) {
7514                     this.removeClass(className[i]);
7515                 }
7516             }else{
7517                 if(this.hasClass(className)){
7518                     var re = this.classReCache[className];
7519                     if (!re) {
7520                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7521                        this.classReCache[className] = re;
7522                     }
7523                     this.dom.className =
7524                         this.dom.className.replace(re, " ");
7525                 }
7526             }
7527             return this;
7528         },
7529
7530         // private
7531         classReCache: {},
7532
7533         /**
7534          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7535          * @param {String} className The CSS class to toggle
7536          * @return {Roo.Element} this
7537          */
7538         toggleClass : function(className){
7539             if(this.hasClass(className)){
7540                 this.removeClass(className);
7541             }else{
7542                 this.addClass(className);
7543             }
7544             return this;
7545         },
7546
7547         /**
7548          * Checks if the specified CSS class exists on this element's DOM node.
7549          * @param {String} className The CSS class to check for
7550          * @return {Boolean} True if the class exists, else false
7551          */
7552         hasClass : function(className){
7553             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7554         },
7555
7556         /**
7557          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7558          * @param {String} oldClassName The CSS class to replace
7559          * @param {String} newClassName The replacement CSS class
7560          * @return {Roo.Element} this
7561          */
7562         replaceClass : function(oldClassName, newClassName){
7563             this.removeClass(oldClassName);
7564             this.addClass(newClassName);
7565             return this;
7566         },
7567
7568         /**
7569          * Returns an object with properties matching the styles requested.
7570          * For example, el.getStyles('color', 'font-size', 'width') might return
7571          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7572          * @param {String} style1 A style name
7573          * @param {String} style2 A style name
7574          * @param {String} etc.
7575          * @return {Object} The style object
7576          */
7577         getStyles : function(){
7578             var a = arguments, len = a.length, r = {};
7579             for(var i = 0; i < len; i++){
7580                 r[a[i]] = this.getStyle(a[i]);
7581             }
7582             return r;
7583         },
7584
7585         /**
7586          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7587          * @param {String} property The style property whose value is returned.
7588          * @return {String} The current value of the style property for this element.
7589          */
7590         getStyle : function(){
7591             return view && view.getComputedStyle ?
7592                 function(prop){
7593                     var el = this.dom, v, cs, camel;
7594                     if(prop == 'float'){
7595                         prop = "cssFloat";
7596                     }
7597                     if(el.style && (v = el.style[prop])){
7598                         return v;
7599                     }
7600                     if(cs = view.getComputedStyle(el, "")){
7601                         if(!(camel = propCache[prop])){
7602                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7603                         }
7604                         return cs[camel];
7605                     }
7606                     return null;
7607                 } :
7608                 function(prop){
7609                     var el = this.dom, v, cs, camel;
7610                     if(prop == 'opacity'){
7611                         if(typeof el.style.filter == 'string'){
7612                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7613                             if(m){
7614                                 var fv = parseFloat(m[1]);
7615                                 if(!isNaN(fv)){
7616                                     return fv ? fv / 100 : 0;
7617                                 }
7618                             }
7619                         }
7620                         return 1;
7621                     }else if(prop == 'float'){
7622                         prop = "styleFloat";
7623                     }
7624                     if(!(camel = propCache[prop])){
7625                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7626                     }
7627                     if(v = el.style[camel]){
7628                         return v;
7629                     }
7630                     if(cs = el.currentStyle){
7631                         return cs[camel];
7632                     }
7633                     return null;
7634                 };
7635         }(),
7636
7637         /**
7638          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7639          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7640          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7641          * @return {Roo.Element} this
7642          */
7643         setStyle : function(prop, value){
7644             if(typeof prop == "string"){
7645                 
7646                 if (prop == 'float') {
7647                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7648                     return this;
7649                 }
7650                 
7651                 var camel;
7652                 if(!(camel = propCache[prop])){
7653                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7654                 }
7655                 
7656                 if(camel == 'opacity') {
7657                     this.setOpacity(value);
7658                 }else{
7659                     this.dom.style[camel] = value;
7660                 }
7661             }else{
7662                 for(var style in prop){
7663                     if(typeof prop[style] != "function"){
7664                        this.setStyle(style, prop[style]);
7665                     }
7666                 }
7667             }
7668             return this;
7669         },
7670
7671         /**
7672          * More flexible version of {@link #setStyle} for setting style properties.
7673          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7674          * a function which returns such a specification.
7675          * @return {Roo.Element} this
7676          */
7677         applyStyles : function(style){
7678             Roo.DomHelper.applyStyles(this.dom, style);
7679             return this;
7680         },
7681
7682         /**
7683           * 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).
7684           * @return {Number} The X position of the element
7685           */
7686         getX : function(){
7687             return D.getX(this.dom);
7688         },
7689
7690         /**
7691           * 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).
7692           * @return {Number} The Y position of the element
7693           */
7694         getY : function(){
7695             return D.getY(this.dom);
7696         },
7697
7698         /**
7699           * 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).
7700           * @return {Array} The XY position of the element
7701           */
7702         getXY : function(){
7703             return D.getXY(this.dom);
7704         },
7705
7706         /**
7707          * 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).
7708          * @param {Number} The X position of the element
7709          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7710          * @return {Roo.Element} this
7711          */
7712         setX : function(x, animate){
7713             if(!animate || !A){
7714                 D.setX(this.dom, x);
7715             }else{
7716                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7717             }
7718             return this;
7719         },
7720
7721         /**
7722          * 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).
7723          * @param {Number} The Y position of the element
7724          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7725          * @return {Roo.Element} this
7726          */
7727         setY : function(y, animate){
7728             if(!animate || !A){
7729                 D.setY(this.dom, y);
7730             }else{
7731                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7732             }
7733             return this;
7734         },
7735
7736         /**
7737          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7738          * @param {String} left The left CSS property value
7739          * @return {Roo.Element} this
7740          */
7741         setLeft : function(left){
7742             this.setStyle("left", this.addUnits(left));
7743             return this;
7744         },
7745
7746         /**
7747          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7748          * @param {String} top The top CSS property value
7749          * @return {Roo.Element} this
7750          */
7751         setTop : function(top){
7752             this.setStyle("top", this.addUnits(top));
7753             return this;
7754         },
7755
7756         /**
7757          * Sets the element's CSS right style.
7758          * @param {String} right The right CSS property value
7759          * @return {Roo.Element} this
7760          */
7761         setRight : function(right){
7762             this.setStyle("right", this.addUnits(right));
7763             return this;
7764         },
7765
7766         /**
7767          * Sets the element's CSS bottom style.
7768          * @param {String} bottom The bottom CSS property value
7769          * @return {Roo.Element} this
7770          */
7771         setBottom : function(bottom){
7772             this.setStyle("bottom", this.addUnits(bottom));
7773             return this;
7774         },
7775
7776         /**
7777          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7778          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7779          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7780          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7781          * @return {Roo.Element} this
7782          */
7783         setXY : function(pos, animate){
7784             if(!animate || !A){
7785                 D.setXY(this.dom, pos);
7786             }else{
7787                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7788             }
7789             return this;
7790         },
7791
7792         /**
7793          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7794          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7795          * @param {Number} x X value for new position (coordinates are page-based)
7796          * @param {Number} y Y value for new position (coordinates are page-based)
7797          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7798          * @return {Roo.Element} this
7799          */
7800         setLocation : function(x, y, animate){
7801             this.setXY([x, y], this.preanim(arguments, 2));
7802             return this;
7803         },
7804
7805         /**
7806          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7807          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7808          * @param {Number} x X value for new position (coordinates are page-based)
7809          * @param {Number} y Y value for new position (coordinates are page-based)
7810          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7811          * @return {Roo.Element} this
7812          */
7813         moveTo : function(x, y, animate){
7814             this.setXY([x, y], this.preanim(arguments, 2));
7815             return this;
7816         },
7817
7818         /**
7819          * Returns the region of the given element.
7820          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7821          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7822          */
7823         getRegion : function(){
7824             return D.getRegion(this.dom);
7825         },
7826
7827         /**
7828          * Returns the offset height of the element
7829          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7830          * @return {Number} The element's height
7831          */
7832         getHeight : function(contentHeight){
7833             var h = this.dom.offsetHeight || 0;
7834             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7835         },
7836
7837         /**
7838          * Returns the offset width of the element
7839          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7840          * @return {Number} The element's width
7841          */
7842         getWidth : function(contentWidth){
7843             var w = this.dom.offsetWidth || 0;
7844             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7845         },
7846
7847         /**
7848          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7849          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7850          * if a height has not been set using CSS.
7851          * @return {Number}
7852          */
7853         getComputedHeight : function(){
7854             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7855             if(!h){
7856                 h = parseInt(this.getStyle('height'), 10) || 0;
7857                 if(!this.isBorderBox()){
7858                     h += this.getFrameWidth('tb');
7859                 }
7860             }
7861             return h;
7862         },
7863
7864         /**
7865          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7866          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7867          * if a width has not been set using CSS.
7868          * @return {Number}
7869          */
7870         getComputedWidth : function(){
7871             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7872             if(!w){
7873                 w = parseInt(this.getStyle('width'), 10) || 0;
7874                 if(!this.isBorderBox()){
7875                     w += this.getFrameWidth('lr');
7876                 }
7877             }
7878             return w;
7879         },
7880
7881         /**
7882          * Returns the size of the element.
7883          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7884          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7885          */
7886         getSize : function(contentSize){
7887             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7888         },
7889
7890         /**
7891          * Returns the width and height of the viewport.
7892          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7893          */
7894         getViewSize : function(){
7895             var d = this.dom, doc = document, aw = 0, ah = 0;
7896             if(d == doc || d == doc.body){
7897                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7898             }else{
7899                 return {
7900                     width : d.clientWidth,
7901                     height: d.clientHeight
7902                 };
7903             }
7904         },
7905
7906         /**
7907          * Returns the value of the "value" attribute
7908          * @param {Boolean} asNumber true to parse the value as a number
7909          * @return {String/Number}
7910          */
7911         getValue : function(asNumber){
7912             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7913         },
7914
7915         // private
7916         adjustWidth : function(width){
7917             if(typeof width == "number"){
7918                 if(this.autoBoxAdjust && !this.isBorderBox()){
7919                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7920                 }
7921                 if(width < 0){
7922                     width = 0;
7923                 }
7924             }
7925             return width;
7926         },
7927
7928         // private
7929         adjustHeight : function(height){
7930             if(typeof height == "number"){
7931                if(this.autoBoxAdjust && !this.isBorderBox()){
7932                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7933                }
7934                if(height < 0){
7935                    height = 0;
7936                }
7937             }
7938             return height;
7939         },
7940
7941         /**
7942          * Set the width of the element
7943          * @param {Number} width The new width
7944          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7945          * @return {Roo.Element} this
7946          */
7947         setWidth : function(width, animate){
7948             width = this.adjustWidth(width);
7949             if(!animate || !A){
7950                 this.dom.style.width = this.addUnits(width);
7951             }else{
7952                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7953             }
7954             return this;
7955         },
7956
7957         /**
7958          * Set the height of the element
7959          * @param {Number} height The new height
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963          setHeight : function(height, animate){
7964             height = this.adjustHeight(height);
7965             if(!animate || !A){
7966                 this.dom.style.height = this.addUnits(height);
7967             }else{
7968                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7969             }
7970             return this;
7971         },
7972
7973         /**
7974          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7975          * @param {Number} width The new width
7976          * @param {Number} height The new height
7977          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7978          * @return {Roo.Element} this
7979          */
7980          setSize : function(width, height, animate){
7981             if(typeof width == "object"){ // in case of object from getSize()
7982                 height = width.height; width = width.width;
7983             }
7984             width = this.adjustWidth(width); height = this.adjustHeight(height);
7985             if(!animate || !A){
7986                 this.dom.style.width = this.addUnits(width);
7987                 this.dom.style.height = this.addUnits(height);
7988             }else{
7989                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7990             }
7991             return this;
7992         },
7993
7994         /**
7995          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7996          * @param {Number} x X value for new position (coordinates are page-based)
7997          * @param {Number} y Y value for new position (coordinates are page-based)
7998          * @param {Number} width The new width
7999          * @param {Number} height The new height
8000          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8001          * @return {Roo.Element} this
8002          */
8003         setBounds : function(x, y, width, height, animate){
8004             if(!animate || !A){
8005                 this.setSize(width, height);
8006                 this.setLocation(x, y);
8007             }else{
8008                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8009                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8010                               this.preanim(arguments, 4), 'motion');
8011             }
8012             return this;
8013         },
8014
8015         /**
8016          * 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.
8017          * @param {Roo.lib.Region} region The region to fill
8018          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8019          * @return {Roo.Element} this
8020          */
8021         setRegion : function(region, animate){
8022             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8023             return this;
8024         },
8025
8026         /**
8027          * Appends an event handler
8028          *
8029          * @param {String}   eventName     The type of event to append
8030          * @param {Function} fn        The method the event invokes
8031          * @param {Object} scope       (optional) The scope (this object) of the fn
8032          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8033          */
8034         addListener : function(eventName, fn, scope, options){
8035             if (this.dom) {
8036                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8037             }
8038         },
8039
8040         /**
8041          * Removes an event handler from this element
8042          * @param {String} eventName the type of event to remove
8043          * @param {Function} fn the method the event invokes
8044          * @return {Roo.Element} this
8045          */
8046         removeListener : function(eventName, fn){
8047             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8048             return this;
8049         },
8050
8051         /**
8052          * Removes all previous added listeners from this element
8053          * @return {Roo.Element} this
8054          */
8055         removeAllListeners : function(){
8056             E.purgeElement(this.dom);
8057             return this;
8058         },
8059
8060         relayEvent : function(eventName, observable){
8061             this.on(eventName, function(e){
8062                 observable.fireEvent(eventName, e);
8063             });
8064         },
8065
8066         /**
8067          * Set the opacity of the element
8068          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8069          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8070          * @return {Roo.Element} this
8071          */
8072          setOpacity : function(opacity, animate){
8073             if(!animate || !A){
8074                 var s = this.dom.style;
8075                 if(Roo.isIE){
8076                     s.zoom = 1;
8077                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8078                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8079                 }else{
8080                     s.opacity = opacity;
8081                 }
8082             }else{
8083                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8084             }
8085             return this;
8086         },
8087
8088         /**
8089          * Gets the left X coordinate
8090          * @param {Boolean} local True to get the local css position instead of page coordinate
8091          * @return {Number}
8092          */
8093         getLeft : function(local){
8094             if(!local){
8095                 return this.getX();
8096             }else{
8097                 return parseInt(this.getStyle("left"), 10) || 0;
8098             }
8099         },
8100
8101         /**
8102          * Gets the right X coordinate of the element (element X position + element width)
8103          * @param {Boolean} local True to get the local css position instead of page coordinate
8104          * @return {Number}
8105          */
8106         getRight : function(local){
8107             if(!local){
8108                 return this.getX() + this.getWidth();
8109             }else{
8110                 return (this.getLeft(true) + this.getWidth()) || 0;
8111             }
8112         },
8113
8114         /**
8115          * Gets the top Y coordinate
8116          * @param {Boolean} local True to get the local css position instead of page coordinate
8117          * @return {Number}
8118          */
8119         getTop : function(local) {
8120             if(!local){
8121                 return this.getY();
8122             }else{
8123                 return parseInt(this.getStyle("top"), 10) || 0;
8124             }
8125         },
8126
8127         /**
8128          * Gets the bottom Y coordinate of the element (element Y position + element height)
8129          * @param {Boolean} local True to get the local css position instead of page coordinate
8130          * @return {Number}
8131          */
8132         getBottom : function(local){
8133             if(!local){
8134                 return this.getY() + this.getHeight();
8135             }else{
8136                 return (this.getTop(true) + this.getHeight()) || 0;
8137             }
8138         },
8139
8140         /**
8141         * Initializes positioning on this element. If a desired position is not passed, it will make the
8142         * the element positioned relative IF it is not already positioned.
8143         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8144         * @param {Number} zIndex (optional) The zIndex to apply
8145         * @param {Number} x (optional) Set the page X position
8146         * @param {Number} y (optional) Set the page Y position
8147         */
8148         position : function(pos, zIndex, x, y){
8149             if(!pos){
8150                if(this.getStyle('position') == 'static'){
8151                    this.setStyle('position', 'relative');
8152                }
8153             }else{
8154                 this.setStyle("position", pos);
8155             }
8156             if(zIndex){
8157                 this.setStyle("z-index", zIndex);
8158             }
8159             if(x !== undefined && y !== undefined){
8160                 this.setXY([x, y]);
8161             }else if(x !== undefined){
8162                 this.setX(x);
8163             }else if(y !== undefined){
8164                 this.setY(y);
8165             }
8166         },
8167
8168         /**
8169         * Clear positioning back to the default when the document was loaded
8170         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8171         * @return {Roo.Element} this
8172          */
8173         clearPositioning : function(value){
8174             value = value ||'';
8175             this.setStyle({
8176                 "left": value,
8177                 "right": value,
8178                 "top": value,
8179                 "bottom": value,
8180                 "z-index": "",
8181                 "position" : "static"
8182             });
8183             return this;
8184         },
8185
8186         /**
8187         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8188         * snapshot before performing an update and then restoring the element.
8189         * @return {Object}
8190         */
8191         getPositioning : function(){
8192             var l = this.getStyle("left");
8193             var t = this.getStyle("top");
8194             return {
8195                 "position" : this.getStyle("position"),
8196                 "left" : l,
8197                 "right" : l ? "" : this.getStyle("right"),
8198                 "top" : t,
8199                 "bottom" : t ? "" : this.getStyle("bottom"),
8200                 "z-index" : this.getStyle("z-index")
8201             };
8202         },
8203
8204         /**
8205          * Gets the width of the border(s) for the specified side(s)
8206          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8207          * passing lr would get the border (l)eft width + the border (r)ight width.
8208          * @return {Number} The width of the sides passed added together
8209          */
8210         getBorderWidth : function(side){
8211             return this.addStyles(side, El.borders);
8212         },
8213
8214         /**
8215          * Gets the width of the padding(s) for the specified side(s)
8216          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8217          * passing lr would get the padding (l)eft + the padding (r)ight.
8218          * @return {Number} The padding of the sides passed added together
8219          */
8220         getPadding : function(side){
8221             return this.addStyles(side, El.paddings);
8222         },
8223
8224         /**
8225         * Set positioning with an object returned by getPositioning().
8226         * @param {Object} posCfg
8227         * @return {Roo.Element} this
8228          */
8229         setPositioning : function(pc){
8230             this.applyStyles(pc);
8231             if(pc.right == "auto"){
8232                 this.dom.style.right = "";
8233             }
8234             if(pc.bottom == "auto"){
8235                 this.dom.style.bottom = "";
8236             }
8237             return this;
8238         },
8239
8240         // private
8241         fixDisplay : function(){
8242             if(this.getStyle("display") == "none"){
8243                 this.setStyle("visibility", "hidden");
8244                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8245                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8246                     this.setStyle("display", "block");
8247                 }
8248             }
8249         },
8250
8251         /**
8252          * Quick set left and top adding default units
8253          * @param {String} left The left CSS property value
8254          * @param {String} top The top CSS property value
8255          * @return {Roo.Element} this
8256          */
8257          setLeftTop : function(left, top){
8258             this.dom.style.left = this.addUnits(left);
8259             this.dom.style.top = this.addUnits(top);
8260             return this;
8261         },
8262
8263         /**
8264          * Move this element relative to its current position.
8265          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8266          * @param {Number} distance How far to move the element in pixels
8267          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8268          * @return {Roo.Element} this
8269          */
8270          move : function(direction, distance, animate){
8271             var xy = this.getXY();
8272             direction = direction.toLowerCase();
8273             switch(direction){
8274                 case "l":
8275                 case "left":
8276                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8277                     break;
8278                case "r":
8279                case "right":
8280                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8281                     break;
8282                case "t":
8283                case "top":
8284                case "up":
8285                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8286                     break;
8287                case "b":
8288                case "bottom":
8289                case "down":
8290                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8291                     break;
8292             }
8293             return this;
8294         },
8295
8296         /**
8297          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8298          * @return {Roo.Element} this
8299          */
8300         clip : function(){
8301             if(!this.isClipped){
8302                this.isClipped = true;
8303                this.originalClip = {
8304                    "o": this.getStyle("overflow"),
8305                    "x": this.getStyle("overflow-x"),
8306                    "y": this.getStyle("overflow-y")
8307                };
8308                this.setStyle("overflow", "hidden");
8309                this.setStyle("overflow-x", "hidden");
8310                this.setStyle("overflow-y", "hidden");
8311             }
8312             return this;
8313         },
8314
8315         /**
8316          *  Return clipping (overflow) to original clipping before clip() was called
8317          * @return {Roo.Element} this
8318          */
8319         unclip : function(){
8320             if(this.isClipped){
8321                 this.isClipped = false;
8322                 var o = this.originalClip;
8323                 if(o.o){this.setStyle("overflow", o.o);}
8324                 if(o.x){this.setStyle("overflow-x", o.x);}
8325                 if(o.y){this.setStyle("overflow-y", o.y);}
8326             }
8327             return this;
8328         },
8329
8330
8331         /**
8332          * Gets the x,y coordinates specified by the anchor position on the element.
8333          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8334          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8335          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8336          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8337          * @return {Array} [x, y] An array containing the element's x and y coordinates
8338          */
8339         getAnchorXY : function(anchor, local, s){
8340             //Passing a different size is useful for pre-calculating anchors,
8341             //especially for anchored animations that change the el size.
8342
8343             var w, h, vp = false;
8344             if(!s){
8345                 var d = this.dom;
8346                 if(d == document.body || d == document){
8347                     vp = true;
8348                     w = D.getViewWidth(); h = D.getViewHeight();
8349                 }else{
8350                     w = this.getWidth(); h = this.getHeight();
8351                 }
8352             }else{
8353                 w = s.width;  h = s.height;
8354             }
8355             var x = 0, y = 0, r = Math.round;
8356             switch((anchor || "tl").toLowerCase()){
8357                 case "c":
8358                     x = r(w*.5);
8359                     y = r(h*.5);
8360                 break;
8361                 case "t":
8362                     x = r(w*.5);
8363                     y = 0;
8364                 break;
8365                 case "l":
8366                     x = 0;
8367                     y = r(h*.5);
8368                 break;
8369                 case "r":
8370                     x = w;
8371                     y = r(h*.5);
8372                 break;
8373                 case "b":
8374                     x = r(w*.5);
8375                     y = h;
8376                 break;
8377                 case "tl":
8378                     x = 0;
8379                     y = 0;
8380                 break;
8381                 case "bl":
8382                     x = 0;
8383                     y = h;
8384                 break;
8385                 case "br":
8386                     x = w;
8387                     y = h;
8388                 break;
8389                 case "tr":
8390                     x = w;
8391                     y = 0;
8392                 break;
8393             }
8394             if(local === true){
8395                 return [x, y];
8396             }
8397             if(vp){
8398                 var sc = this.getScroll();
8399                 return [x + sc.left, y + sc.top];
8400             }
8401             //Add the element's offset xy
8402             var o = this.getXY();
8403             return [x+o[0], y+o[1]];
8404         },
8405
8406         /**
8407          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8408          * supported position values.
8409          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8410          * @param {String} position The position to align to.
8411          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8412          * @return {Array} [x, y]
8413          */
8414         getAlignToXY : function(el, p, o){
8415             el = Roo.get(el);
8416             var d = this.dom;
8417             if(!el.dom){
8418                 throw "Element.alignTo with an element that doesn't exist";
8419             }
8420             var c = false; //constrain to viewport
8421             var p1 = "", p2 = "";
8422             o = o || [0,0];
8423
8424             if(!p){
8425                 p = "tl-bl";
8426             }else if(p == "?"){
8427                 p = "tl-bl?";
8428             }else if(p.indexOf("-") == -1){
8429                 p = "tl-" + p;
8430             }
8431             p = p.toLowerCase();
8432             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8433             if(!m){
8434                throw "Element.alignTo with an invalid alignment " + p;
8435             }
8436             p1 = m[1]; p2 = m[2]; c = !!m[3];
8437
8438             //Subtract the aligned el's internal xy from the target's offset xy
8439             //plus custom offset to get the aligned el's new offset xy
8440             var a1 = this.getAnchorXY(p1, true);
8441             var a2 = el.getAnchorXY(p2, false);
8442             var x = a2[0] - a1[0] + o[0];
8443             var y = a2[1] - a1[1] + o[1];
8444             if(c){
8445                 //constrain the aligned el to viewport if necessary
8446                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8447                 // 5px of margin for ie
8448                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8449
8450                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8451                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8452                 //otherwise swap the aligned el to the opposite border of the target.
8453                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8454                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8455                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8456                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8457
8458                var doc = document;
8459                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8460                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8461
8462                if((x+w) > dw + scrollX){
8463                     x = swapX ? r.left-w : dw+scrollX-w;
8464                 }
8465                if(x < scrollX){
8466                    x = swapX ? r.right : scrollX;
8467                }
8468                if((y+h) > dh + scrollY){
8469                     y = swapY ? r.top-h : dh+scrollY-h;
8470                 }
8471                if (y < scrollY){
8472                    y = swapY ? r.bottom : scrollY;
8473                }
8474             }
8475             return [x,y];
8476         },
8477
8478         // private
8479         getConstrainToXY : function(){
8480             var os = {top:0, left:0, bottom:0, right: 0};
8481
8482             return function(el, local, offsets, proposedXY){
8483                 el = Roo.get(el);
8484                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8485
8486                 var vw, vh, vx = 0, vy = 0;
8487                 if(el.dom == document.body || el.dom == document){
8488                     vw = Roo.lib.Dom.getViewWidth();
8489                     vh = Roo.lib.Dom.getViewHeight();
8490                 }else{
8491                     vw = el.dom.clientWidth;
8492                     vh = el.dom.clientHeight;
8493                     if(!local){
8494                         var vxy = el.getXY();
8495                         vx = vxy[0];
8496                         vy = vxy[1];
8497                     }
8498                 }
8499
8500                 var s = el.getScroll();
8501
8502                 vx += offsets.left + s.left;
8503                 vy += offsets.top + s.top;
8504
8505                 vw -= offsets.right;
8506                 vh -= offsets.bottom;
8507
8508                 var vr = vx+vw;
8509                 var vb = vy+vh;
8510
8511                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8512                 var x = xy[0], y = xy[1];
8513                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8514
8515                 // only move it if it needs it
8516                 var moved = false;
8517
8518                 // first validate right/bottom
8519                 if((x + w) > vr){
8520                     x = vr - w;
8521                     moved = true;
8522                 }
8523                 if((y + h) > vb){
8524                     y = vb - h;
8525                     moved = true;
8526                 }
8527                 // then make sure top/left isn't negative
8528                 if(x < vx){
8529                     x = vx;
8530                     moved = true;
8531                 }
8532                 if(y < vy){
8533                     y = vy;
8534                     moved = true;
8535                 }
8536                 return moved ? [x, y] : false;
8537             };
8538         }(),
8539
8540         // private
8541         adjustForConstraints : function(xy, parent, offsets){
8542             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8543         },
8544
8545         /**
8546          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8547          * document it aligns it to the viewport.
8548          * The position parameter is optional, and can be specified in any one of the following formats:
8549          * <ul>
8550          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8551          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8552          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8553          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8554          *   <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
8555          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8556          * </ul>
8557          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8558          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8559          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8560          * that specified in order to enforce the viewport constraints.
8561          * Following are all of the supported anchor positions:
8562     <pre>
8563     Value  Description
8564     -----  -----------------------------
8565     tl     The top left corner (default)
8566     t      The center of the top edge
8567     tr     The top right corner
8568     l      The center of the left edge
8569     c      In the center of the element
8570     r      The center of the right edge
8571     bl     The bottom left corner
8572     b      The center of the bottom edge
8573     br     The bottom right corner
8574     </pre>
8575     Example Usage:
8576     <pre><code>
8577     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8578     el.alignTo("other-el");
8579
8580     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8581     el.alignTo("other-el", "tr?");
8582
8583     // align the bottom right corner of el with the center left edge of other-el
8584     el.alignTo("other-el", "br-l?");
8585
8586     // align the center of el with the bottom left corner of other-el and
8587     // adjust the x position by -6 pixels (and the y position by 0)
8588     el.alignTo("other-el", "c-bl", [-6, 0]);
8589     </code></pre>
8590          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8591          * @param {String} position The position to align to.
8592          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8593          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8594          * @return {Roo.Element} this
8595          */
8596         alignTo : function(element, position, offsets, animate){
8597             var xy = this.getAlignToXY(element, position, offsets);
8598             this.setXY(xy, this.preanim(arguments, 3));
8599             return this;
8600         },
8601
8602         /**
8603          * Anchors an element to another element and realigns it when the window is resized.
8604          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8605          * @param {String} position The position to align to.
8606          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8607          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8608          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8609          * is a number, it is used as the buffer delay (defaults to 50ms).
8610          * @param {Function} callback The function to call after the animation finishes
8611          * @return {Roo.Element} this
8612          */
8613         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8614             var action = function(){
8615                 this.alignTo(el, alignment, offsets, animate);
8616                 Roo.callback(callback, this);
8617             };
8618             Roo.EventManager.onWindowResize(action, this);
8619             var tm = typeof monitorScroll;
8620             if(tm != 'undefined'){
8621                 Roo.EventManager.on(window, 'scroll', action, this,
8622                     {buffer: tm == 'number' ? monitorScroll : 50});
8623             }
8624             action.call(this); // align immediately
8625             return this;
8626         },
8627         /**
8628          * Clears any opacity settings from this element. Required in some cases for IE.
8629          * @return {Roo.Element} this
8630          */
8631         clearOpacity : function(){
8632             if (window.ActiveXObject) {
8633                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8634                     this.dom.style.filter = "";
8635                 }
8636             } else {
8637                 this.dom.style.opacity = "";
8638                 this.dom.style["-moz-opacity"] = "";
8639                 this.dom.style["-khtml-opacity"] = "";
8640             }
8641             return this;
8642         },
8643
8644         /**
8645          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8646          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8647          * @return {Roo.Element} this
8648          */
8649         hide : function(animate){
8650             this.setVisible(false, this.preanim(arguments, 0));
8651             return this;
8652         },
8653
8654         /**
8655         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8656         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8657          * @return {Roo.Element} this
8658          */
8659         show : function(animate){
8660             this.setVisible(true, this.preanim(arguments, 0));
8661             return this;
8662         },
8663
8664         /**
8665          * @private Test if size has a unit, otherwise appends the default
8666          */
8667         addUnits : function(size){
8668             return Roo.Element.addUnits(size, this.defaultUnit);
8669         },
8670
8671         /**
8672          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8673          * @return {Roo.Element} this
8674          */
8675         beginMeasure : function(){
8676             var el = this.dom;
8677             if(el.offsetWidth || el.offsetHeight){
8678                 return this; // offsets work already
8679             }
8680             var changed = [];
8681             var p = this.dom, b = document.body; // start with this element
8682             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8683                 var pe = Roo.get(p);
8684                 if(pe.getStyle('display') == 'none'){
8685                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8686                     p.style.visibility = "hidden";
8687                     p.style.display = "block";
8688                 }
8689                 p = p.parentNode;
8690             }
8691             this._measureChanged = changed;
8692             return this;
8693
8694         },
8695
8696         /**
8697          * Restores displays to before beginMeasure was called
8698          * @return {Roo.Element} this
8699          */
8700         endMeasure : function(){
8701             var changed = this._measureChanged;
8702             if(changed){
8703                 for(var i = 0, len = changed.length; i < len; i++) {
8704                     var r = changed[i];
8705                     r.el.style.visibility = r.visibility;
8706                     r.el.style.display = "none";
8707                 }
8708                 this._measureChanged = null;
8709             }
8710             return this;
8711         },
8712
8713         /**
8714         * Update the innerHTML of this element, optionally searching for and processing scripts
8715         * @param {String} html The new HTML
8716         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8717         * @param {Function} callback For async script loading you can be noticed when the update completes
8718         * @return {Roo.Element} this
8719          */
8720         update : function(html, loadScripts, callback){
8721             if(typeof html == "undefined"){
8722                 html = "";
8723             }
8724             if(loadScripts !== true){
8725                 this.dom.innerHTML = html;
8726                 if(typeof callback == "function"){
8727                     callback();
8728                 }
8729                 return this;
8730             }
8731             var id = Roo.id();
8732             var dom = this.dom;
8733
8734             html += '<span id="' + id + '"></span>';
8735
8736             E.onAvailable(id, function(){
8737                 var hd = document.getElementsByTagName("head")[0];
8738                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8739                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8740                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8741
8742                 var match;
8743                 while(match = re.exec(html)){
8744                     var attrs = match[1];
8745                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8746                     if(srcMatch && srcMatch[2]){
8747                        var s = document.createElement("script");
8748                        s.src = srcMatch[2];
8749                        var typeMatch = attrs.match(typeRe);
8750                        if(typeMatch && typeMatch[2]){
8751                            s.type = typeMatch[2];
8752                        }
8753                        hd.appendChild(s);
8754                     }else if(match[2] && match[2].length > 0){
8755                         if(window.execScript) {
8756                            window.execScript(match[2]);
8757                         } else {
8758                             /**
8759                              * eval:var:id
8760                              * eval:var:dom
8761                              * eval:var:html
8762                              * 
8763                              */
8764                            window.eval(match[2]);
8765                         }
8766                     }
8767                 }
8768                 var el = document.getElementById(id);
8769                 if(el){el.parentNode.removeChild(el);}
8770                 if(typeof callback == "function"){
8771                     callback();
8772                 }
8773             });
8774             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8775             return this;
8776         },
8777
8778         /**
8779          * Direct access to the UpdateManager update() method (takes the same parameters).
8780          * @param {String/Function} url The url for this request or a function to call to get the url
8781          * @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}
8782          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8783          * @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.
8784          * @return {Roo.Element} this
8785          */
8786         load : function(){
8787             var um = this.getUpdateManager();
8788             um.update.apply(um, arguments);
8789             return this;
8790         },
8791
8792         /**
8793         * Gets this element's UpdateManager
8794         * @return {Roo.UpdateManager} The UpdateManager
8795         */
8796         getUpdateManager : function(){
8797             if(!this.updateManager){
8798                 this.updateManager = new Roo.UpdateManager(this);
8799             }
8800             return this.updateManager;
8801         },
8802
8803         /**
8804          * Disables text selection for this element (normalized across browsers)
8805          * @return {Roo.Element} this
8806          */
8807         unselectable : function(){
8808             this.dom.unselectable = "on";
8809             this.swallowEvent("selectstart", true);
8810             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8811             this.addClass("x-unselectable");
8812             return this;
8813         },
8814
8815         /**
8816         * Calculates the x, y to center this element on the screen
8817         * @return {Array} The x, y values [x, y]
8818         */
8819         getCenterXY : function(){
8820             return this.getAlignToXY(document, 'c-c');
8821         },
8822
8823         /**
8824         * Centers the Element in either the viewport, or another Element.
8825         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8826         */
8827         center : function(centerIn){
8828             this.alignTo(centerIn || document, 'c-c');
8829             return this;
8830         },
8831
8832         /**
8833          * Tests various css rules/browsers to determine if this element uses a border box
8834          * @return {Boolean}
8835          */
8836         isBorderBox : function(){
8837             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8838         },
8839
8840         /**
8841          * Return a box {x, y, width, height} that can be used to set another elements
8842          * size/location to match this element.
8843          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8844          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8845          * @return {Object} box An object in the format {x, y, width, height}
8846          */
8847         getBox : function(contentBox, local){
8848             var xy;
8849             if(!local){
8850                 xy = this.getXY();
8851             }else{
8852                 var left = parseInt(this.getStyle("left"), 10) || 0;
8853                 var top = parseInt(this.getStyle("top"), 10) || 0;
8854                 xy = [left, top];
8855             }
8856             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8857             if(!contentBox){
8858                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8859             }else{
8860                 var l = this.getBorderWidth("l")+this.getPadding("l");
8861                 var r = this.getBorderWidth("r")+this.getPadding("r");
8862                 var t = this.getBorderWidth("t")+this.getPadding("t");
8863                 var b = this.getBorderWidth("b")+this.getPadding("b");
8864                 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)};
8865             }
8866             bx.right = bx.x + bx.width;
8867             bx.bottom = bx.y + bx.height;
8868             return bx;
8869         },
8870
8871         /**
8872          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8873          for more information about the sides.
8874          * @param {String} sides
8875          * @return {Number}
8876          */
8877         getFrameWidth : function(sides, onlyContentBox){
8878             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8879         },
8880
8881         /**
8882          * 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.
8883          * @param {Object} box The box to fill {x, y, width, height}
8884          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8885          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8886          * @return {Roo.Element} this
8887          */
8888         setBox : function(box, adjust, animate){
8889             var w = box.width, h = box.height;
8890             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8891                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8892                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8893             }
8894             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8895             return this;
8896         },
8897
8898         /**
8899          * Forces the browser to repaint this element
8900          * @return {Roo.Element} this
8901          */
8902          repaint : function(){
8903             var dom = this.dom;
8904             this.addClass("x-repaint");
8905             setTimeout(function(){
8906                 Roo.get(dom).removeClass("x-repaint");
8907             }, 1);
8908             return this;
8909         },
8910
8911         /**
8912          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8913          * then it returns the calculated width of the sides (see getPadding)
8914          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8915          * @return {Object/Number}
8916          */
8917         getMargins : function(side){
8918             if(!side){
8919                 return {
8920                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8921                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8922                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8923                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8924                 };
8925             }else{
8926                 return this.addStyles(side, El.margins);
8927              }
8928         },
8929
8930         // private
8931         addStyles : function(sides, styles){
8932             var val = 0, v, w;
8933             for(var i = 0, len = sides.length; i < len; i++){
8934                 v = this.getStyle(styles[sides.charAt(i)]);
8935                 if(v){
8936                      w = parseInt(v, 10);
8937                      if(w){ val += w; }
8938                 }
8939             }
8940             return val;
8941         },
8942
8943         /**
8944          * Creates a proxy element of this element
8945          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8946          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8947          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8948          * @return {Roo.Element} The new proxy element
8949          */
8950         createProxy : function(config, renderTo, matchBox){
8951             if(renderTo){
8952                 renderTo = Roo.getDom(renderTo);
8953             }else{
8954                 renderTo = document.body;
8955             }
8956             config = typeof config == "object" ?
8957                 config : {tag : "div", cls: config};
8958             var proxy = Roo.DomHelper.append(renderTo, config, true);
8959             if(matchBox){
8960                proxy.setBox(this.getBox());
8961             }
8962             return proxy;
8963         },
8964
8965         /**
8966          * Puts a mask over this element to disable user interaction. Requires core.css.
8967          * This method can only be applied to elements which accept child nodes.
8968          * @param {String} msg (optional) A message to display in the mask
8969          * @param {String} msgCls (optional) A css class to apply to the msg element
8970          * @return {Element} The mask  element
8971          */
8972         mask : function(msg, msgCls)
8973         {
8974             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
8975                 this.setStyle("position", "relative");
8976             }
8977             if(!this._mask){
8978                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8979             }
8980             this.addClass("x-masked");
8981             this._mask.setDisplayed(true);
8982             
8983             // we wander
8984             var z = 0;
8985             var dom = this.dom
8986             while (dom && dom.style) {
8987                 if (!isNaN(parseInt(dom.style.zIndex))) {
8988                     z = Math.max(z, parseInt(dom.style.zIndex));
8989                 }
8990                 dom = dom.parentNode;
8991             }
8992             // if we are masking the body - then it hides everything..
8993             if (this.dom == document.body) {
8994                 z = 1000000;
8995                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8996                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8997             }
8998            
8999             if(typeof msg == 'string'){
9000                 if(!this._maskMsg){
9001                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9002                 }
9003                 var mm = this._maskMsg;
9004                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9005                 if (mm.dom.firstChild) { // weird IE issue?
9006                     mm.dom.firstChild.innerHTML = msg;
9007                 }
9008                 mm.setDisplayed(true);
9009                 mm.center(this);
9010                 mm.setStyle('z-index', z + 102);
9011             }
9012             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9013                 this._mask.setHeight(this.getHeight());
9014             }
9015             this._mask.setStyle('z-index', z + 100);
9016             
9017             return this._mask;
9018         },
9019
9020         /**
9021          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9022          * it is cached for reuse.
9023          */
9024         unmask : function(removeEl){
9025             if(this._mask){
9026                 if(removeEl === true){
9027                     this._mask.remove();
9028                     delete this._mask;
9029                     if(this._maskMsg){
9030                         this._maskMsg.remove();
9031                         delete this._maskMsg;
9032                     }
9033                 }else{
9034                     this._mask.setDisplayed(false);
9035                     if(this._maskMsg){
9036                         this._maskMsg.setDisplayed(false);
9037                     }
9038                 }
9039             }
9040             this.removeClass("x-masked");
9041         },
9042
9043         /**
9044          * Returns true if this element is masked
9045          * @return {Boolean}
9046          */
9047         isMasked : function(){
9048             return this._mask && this._mask.isVisible();
9049         },
9050
9051         /**
9052          * Creates an iframe shim for this element to keep selects and other windowed objects from
9053          * showing through.
9054          * @return {Roo.Element} The new shim element
9055          */
9056         createShim : function(){
9057             var el = document.createElement('iframe');
9058             el.frameBorder = 'no';
9059             el.className = 'roo-shim';
9060             if(Roo.isIE && Roo.isSecure){
9061                 el.src = Roo.SSL_SECURE_URL;
9062             }
9063             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9064             shim.autoBoxAdjust = false;
9065             return shim;
9066         },
9067
9068         /**
9069          * Removes this element from the DOM and deletes it from the cache
9070          */
9071         remove : function(){
9072             if(this.dom.parentNode){
9073                 this.dom.parentNode.removeChild(this.dom);
9074             }
9075             delete El.cache[this.dom.id];
9076         },
9077
9078         /**
9079          * Sets up event handlers to add and remove a css class when the mouse is over this element
9080          * @param {String} className
9081          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9082          * mouseout events for children elements
9083          * @return {Roo.Element} this
9084          */
9085         addClassOnOver : function(className, preventFlicker){
9086             this.on("mouseover", function(){
9087                 Roo.fly(this, '_internal').addClass(className);
9088             }, this.dom);
9089             var removeFn = function(e){
9090                 if(preventFlicker !== true || !e.within(this, true)){
9091                     Roo.fly(this, '_internal').removeClass(className);
9092                 }
9093             };
9094             this.on("mouseout", removeFn, this.dom);
9095             return this;
9096         },
9097
9098         /**
9099          * Sets up event handlers to add and remove a css class when this element has the focus
9100          * @param {String} className
9101          * @return {Roo.Element} this
9102          */
9103         addClassOnFocus : function(className){
9104             this.on("focus", function(){
9105                 Roo.fly(this, '_internal').addClass(className);
9106             }, this.dom);
9107             this.on("blur", function(){
9108                 Roo.fly(this, '_internal').removeClass(className);
9109             }, this.dom);
9110             return this;
9111         },
9112         /**
9113          * 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)
9114          * @param {String} className
9115          * @return {Roo.Element} this
9116          */
9117         addClassOnClick : function(className){
9118             var dom = this.dom;
9119             this.on("mousedown", function(){
9120                 Roo.fly(dom, '_internal').addClass(className);
9121                 var d = Roo.get(document);
9122                 var fn = function(){
9123                     Roo.fly(dom, '_internal').removeClass(className);
9124                     d.removeListener("mouseup", fn);
9125                 };
9126                 d.on("mouseup", fn);
9127             });
9128             return this;
9129         },
9130
9131         /**
9132          * Stops the specified event from bubbling and optionally prevents the default action
9133          * @param {String} eventName
9134          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9135          * @return {Roo.Element} this
9136          */
9137         swallowEvent : function(eventName, preventDefault){
9138             var fn = function(e){
9139                 e.stopPropagation();
9140                 if(preventDefault){
9141                     e.preventDefault();
9142                 }
9143             };
9144             if(eventName instanceof Array){
9145                 for(var i = 0, len = eventName.length; i < len; i++){
9146                      this.on(eventName[i], fn);
9147                 }
9148                 return this;
9149             }
9150             this.on(eventName, fn);
9151             return this;
9152         },
9153
9154         /**
9155          * @private
9156          */
9157       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9158
9159         /**
9160          * Sizes this element to its parent element's dimensions performing
9161          * neccessary box adjustments.
9162          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9163          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9164          * @return {Roo.Element} this
9165          */
9166         fitToParent : function(monitorResize, targetParent) {
9167           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9168           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9169           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9170             return;
9171           }
9172           var p = Roo.get(targetParent || this.dom.parentNode);
9173           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9174           if (monitorResize === true) {
9175             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9176             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9177           }
9178           return this;
9179         },
9180
9181         /**
9182          * Gets the next sibling, skipping text nodes
9183          * @return {HTMLElement} The next sibling or null
9184          */
9185         getNextSibling : function(){
9186             var n = this.dom.nextSibling;
9187             while(n && n.nodeType != 1){
9188                 n = n.nextSibling;
9189             }
9190             return n;
9191         },
9192
9193         /**
9194          * Gets the previous sibling, skipping text nodes
9195          * @return {HTMLElement} The previous sibling or null
9196          */
9197         getPrevSibling : function(){
9198             var n = this.dom.previousSibling;
9199             while(n && n.nodeType != 1){
9200                 n = n.previousSibling;
9201             }
9202             return n;
9203         },
9204
9205
9206         /**
9207          * Appends the passed element(s) to this element
9208          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9209          * @return {Roo.Element} this
9210          */
9211         appendChild: function(el){
9212             el = Roo.get(el);
9213             el.appendTo(this);
9214             return this;
9215         },
9216
9217         /**
9218          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9219          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9220          * automatically generated with the specified attributes.
9221          * @param {HTMLElement} insertBefore (optional) a child element of this element
9222          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9223          * @return {Roo.Element} The new child element
9224          */
9225         createChild: function(config, insertBefore, returnDom){
9226             config = config || {tag:'div'};
9227             if(insertBefore){
9228                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9229             }
9230             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9231         },
9232
9233         /**
9234          * Appends this element to the passed element
9235          * @param {String/HTMLElement/Element} el The new parent element
9236          * @return {Roo.Element} this
9237          */
9238         appendTo: function(el){
9239             el = Roo.getDom(el);
9240             el.appendChild(this.dom);
9241             return this;
9242         },
9243
9244         /**
9245          * Inserts this element before the passed element in the DOM
9246          * @param {String/HTMLElement/Element} el The element to insert before
9247          * @return {Roo.Element} this
9248          */
9249         insertBefore: function(el){
9250             el = Roo.getDom(el);
9251             el.parentNode.insertBefore(this.dom, el);
9252             return this;
9253         },
9254
9255         /**
9256          * Inserts this element after the passed element in the DOM
9257          * @param {String/HTMLElement/Element} el The element to insert after
9258          * @return {Roo.Element} this
9259          */
9260         insertAfter: function(el){
9261             el = Roo.getDom(el);
9262             el.parentNode.insertBefore(this.dom, el.nextSibling);
9263             return this;
9264         },
9265
9266         /**
9267          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9268          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9269          * @return {Roo.Element} The new child
9270          */
9271         insertFirst: function(el, returnDom){
9272             el = el || {};
9273             if(typeof el == 'object' && !el.nodeType){ // dh config
9274                 return this.createChild(el, this.dom.firstChild, returnDom);
9275             }else{
9276                 el = Roo.getDom(el);
9277                 this.dom.insertBefore(el, this.dom.firstChild);
9278                 return !returnDom ? Roo.get(el) : el;
9279             }
9280         },
9281
9282         /**
9283          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9284          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9285          * @param {String} where (optional) 'before' or 'after' defaults to before
9286          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9287          * @return {Roo.Element} the inserted Element
9288          */
9289         insertSibling: function(el, where, returnDom){
9290             where = where ? where.toLowerCase() : 'before';
9291             el = el || {};
9292             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9293
9294             if(typeof el == 'object' && !el.nodeType){ // dh config
9295                 if(where == 'after' && !this.dom.nextSibling){
9296                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9297                 }else{
9298                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9299                 }
9300
9301             }else{
9302                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9303                             where == 'before' ? this.dom : this.dom.nextSibling);
9304                 if(!returnDom){
9305                     rt = Roo.get(rt);
9306                 }
9307             }
9308             return rt;
9309         },
9310
9311         /**
9312          * Creates and wraps this element with another element
9313          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9314          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9315          * @return {HTMLElement/Element} The newly created wrapper element
9316          */
9317         wrap: function(config, returnDom){
9318             if(!config){
9319                 config = {tag: "div"};
9320             }
9321             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9322             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9323             return newEl;
9324         },
9325
9326         /**
9327          * Replaces the passed element with this element
9328          * @param {String/HTMLElement/Element} el The element to replace
9329          * @return {Roo.Element} this
9330          */
9331         replace: function(el){
9332             el = Roo.get(el);
9333             this.insertBefore(el);
9334             el.remove();
9335             return this;
9336         },
9337
9338         /**
9339          * Inserts an html fragment into this element
9340          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9341          * @param {String} html The HTML fragment
9342          * @param {Boolean} returnEl True to return an Roo.Element
9343          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9344          */
9345         insertHtml : function(where, html, returnEl){
9346             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9347             return returnEl ? Roo.get(el) : el;
9348         },
9349
9350         /**
9351          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9352          * @param {Object} o The object with the attributes
9353          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9354          * @return {Roo.Element} this
9355          */
9356         set : function(o, useSet){
9357             var el = this.dom;
9358             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9359             for(var attr in o){
9360                 if(attr == "style" || typeof o[attr] == "function") continue;
9361                 if(attr=="cls"){
9362                     el.className = o["cls"];
9363                 }else{
9364                     if(useSet) el.setAttribute(attr, o[attr]);
9365                     else el[attr] = o[attr];
9366                 }
9367             }
9368             if(o.style){
9369                 Roo.DomHelper.applyStyles(el, o.style);
9370             }
9371             return this;
9372         },
9373
9374         /**
9375          * Convenience method for constructing a KeyMap
9376          * @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:
9377          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9378          * @param {Function} fn The function to call
9379          * @param {Object} scope (optional) The scope of the function
9380          * @return {Roo.KeyMap} The KeyMap created
9381          */
9382         addKeyListener : function(key, fn, scope){
9383             var config;
9384             if(typeof key != "object" || key instanceof Array){
9385                 config = {
9386                     key: key,
9387                     fn: fn,
9388                     scope: scope
9389                 };
9390             }else{
9391                 config = {
9392                     key : key.key,
9393                     shift : key.shift,
9394                     ctrl : key.ctrl,
9395                     alt : key.alt,
9396                     fn: fn,
9397                     scope: scope
9398                 };
9399             }
9400             return new Roo.KeyMap(this, config);
9401         },
9402
9403         /**
9404          * Creates a KeyMap for this element
9405          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9406          * @return {Roo.KeyMap} The KeyMap created
9407          */
9408         addKeyMap : function(config){
9409             return new Roo.KeyMap(this, config);
9410         },
9411
9412         /**
9413          * Returns true if this element is scrollable.
9414          * @return {Boolean}
9415          */
9416          isScrollable : function(){
9417             var dom = this.dom;
9418             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9419         },
9420
9421         /**
9422          * 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().
9423          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9424          * @param {Number} value The new scroll value
9425          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9426          * @return {Element} this
9427          */
9428
9429         scrollTo : function(side, value, animate){
9430             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9431             if(!animate || !A){
9432                 this.dom[prop] = value;
9433             }else{
9434                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9435                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9436             }
9437             return this;
9438         },
9439
9440         /**
9441          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9442          * within this element's scrollable range.
9443          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9444          * @param {Number} distance How far to scroll the element in pixels
9445          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9446          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9447          * was scrolled as far as it could go.
9448          */
9449          scroll : function(direction, distance, animate){
9450              if(!this.isScrollable()){
9451                  return;
9452              }
9453              var el = this.dom;
9454              var l = el.scrollLeft, t = el.scrollTop;
9455              var w = el.scrollWidth, h = el.scrollHeight;
9456              var cw = el.clientWidth, ch = el.clientHeight;
9457              direction = direction.toLowerCase();
9458              var scrolled = false;
9459              var a = this.preanim(arguments, 2);
9460              switch(direction){
9461                  case "l":
9462                  case "left":
9463                      if(w - l > cw){
9464                          var v = Math.min(l + distance, w-cw);
9465                          this.scrollTo("left", v, a);
9466                          scrolled = true;
9467                      }
9468                      break;
9469                 case "r":
9470                 case "right":
9471                      if(l > 0){
9472                          var v = Math.max(l - distance, 0);
9473                          this.scrollTo("left", v, a);
9474                          scrolled = true;
9475                      }
9476                      break;
9477                 case "t":
9478                 case "top":
9479                 case "up":
9480                      if(t > 0){
9481                          var v = Math.max(t - distance, 0);
9482                          this.scrollTo("top", v, a);
9483                          scrolled = true;
9484                      }
9485                      break;
9486                 case "b":
9487                 case "bottom":
9488                 case "down":
9489                      if(h - t > ch){
9490                          var v = Math.min(t + distance, h-ch);
9491                          this.scrollTo("top", v, a);
9492                          scrolled = true;
9493                      }
9494                      break;
9495              }
9496              return scrolled;
9497         },
9498
9499         /**
9500          * Translates the passed page coordinates into left/top css values for this element
9501          * @param {Number/Array} x The page x or an array containing [x, y]
9502          * @param {Number} y The page y
9503          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9504          */
9505         translatePoints : function(x, y){
9506             if(typeof x == 'object' || x instanceof Array){
9507                 y = x[1]; x = x[0];
9508             }
9509             var p = this.getStyle('position');
9510             var o = this.getXY();
9511
9512             var l = parseInt(this.getStyle('left'), 10);
9513             var t = parseInt(this.getStyle('top'), 10);
9514
9515             if(isNaN(l)){
9516                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9517             }
9518             if(isNaN(t)){
9519                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9520             }
9521
9522             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9523         },
9524
9525         /**
9526          * Returns the current scroll position of the element.
9527          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9528          */
9529         getScroll : function(){
9530             var d = this.dom, doc = document;
9531             if(d == doc || d == doc.body){
9532                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9533                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9534                 return {left: l, top: t};
9535             }else{
9536                 return {left: d.scrollLeft, top: d.scrollTop};
9537             }
9538         },
9539
9540         /**
9541          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9542          * are convert to standard 6 digit hex color.
9543          * @param {String} attr The css attribute
9544          * @param {String} defaultValue The default value to use when a valid color isn't found
9545          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9546          * YUI color anims.
9547          */
9548         getColor : function(attr, defaultValue, prefix){
9549             var v = this.getStyle(attr);
9550             if(!v || v == "transparent" || v == "inherit") {
9551                 return defaultValue;
9552             }
9553             var color = typeof prefix == "undefined" ? "#" : prefix;
9554             if(v.substr(0, 4) == "rgb("){
9555                 var rvs = v.slice(4, v.length -1).split(",");
9556                 for(var i = 0; i < 3; i++){
9557                     var h = parseInt(rvs[i]).toString(16);
9558                     if(h < 16){
9559                         h = "0" + h;
9560                     }
9561                     color += h;
9562                 }
9563             } else {
9564                 if(v.substr(0, 1) == "#"){
9565                     if(v.length == 4) {
9566                         for(var i = 1; i < 4; i++){
9567                             var c = v.charAt(i);
9568                             color +=  c + c;
9569                         }
9570                     }else if(v.length == 7){
9571                         color += v.substr(1);
9572                     }
9573                 }
9574             }
9575             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9576         },
9577
9578         /**
9579          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9580          * gradient background, rounded corners and a 4-way shadow.
9581          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9582          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9583          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9584          * @return {Roo.Element} this
9585          */
9586         boxWrap : function(cls){
9587             cls = cls || 'x-box';
9588             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9589             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9590             return el;
9591         },
9592
9593         /**
9594          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9595          * @param {String} namespace The namespace in which to look for the attribute
9596          * @param {String} name The attribute name
9597          * @return {String} The attribute value
9598          */
9599         getAttributeNS : Roo.isIE ? function(ns, name){
9600             var d = this.dom;
9601             var type = typeof d[ns+":"+name];
9602             if(type != 'undefined' && type != 'unknown'){
9603                 return d[ns+":"+name];
9604             }
9605             return d[name];
9606         } : function(ns, name){
9607             var d = this.dom;
9608             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9609         },
9610         
9611         
9612         /**
9613          * Sets or Returns the value the dom attribute value
9614          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9615          * @param {String} value (optional) The value to set the attribute to
9616          * @return {String} The attribute value
9617          */
9618         attr : function(name){
9619             if (arguments.length > 1) {
9620                 this.dom.setAttribute(name, arguments[1]);
9621                 return arguments[1];
9622             }
9623             if (typeof(name) == 'object') {
9624                 for(var i in name) {
9625                     this.attr(i, name[i]);
9626                 }
9627                 return name;
9628             }
9629             
9630             
9631             if (!this.dom.hasAttribute(name)) {
9632                 return undefined;
9633             }
9634             return this.dom.getAttribute(name);
9635         }
9636         
9637         
9638         
9639     };
9640
9641     var ep = El.prototype;
9642
9643     /**
9644      * Appends an event handler (Shorthand for addListener)
9645      * @param {String}   eventName     The type of event to append
9646      * @param {Function} fn        The method the event invokes
9647      * @param {Object} scope       (optional) The scope (this object) of the fn
9648      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9649      * @method
9650      */
9651     ep.on = ep.addListener;
9652         // backwards compat
9653     ep.mon = ep.addListener;
9654
9655     /**
9656      * Removes an event handler from this element (shorthand for removeListener)
9657      * @param {String} eventName the type of event to remove
9658      * @param {Function} fn the method the event invokes
9659      * @return {Roo.Element} this
9660      * @method
9661      */
9662     ep.un = ep.removeListener;
9663
9664     /**
9665      * true to automatically adjust width and height settings for box-model issues (default to true)
9666      */
9667     ep.autoBoxAdjust = true;
9668
9669     // private
9670     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9671
9672     // private
9673     El.addUnits = function(v, defaultUnit){
9674         if(v === "" || v == "auto"){
9675             return v;
9676         }
9677         if(v === undefined){
9678             return '';
9679         }
9680         if(typeof v == "number" || !El.unitPattern.test(v)){
9681             return v + (defaultUnit || 'px');
9682         }
9683         return v;
9684     };
9685
9686     // special markup used throughout Roo when box wrapping elements
9687     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>';
9688     /**
9689      * Visibility mode constant - Use visibility to hide element
9690      * @static
9691      * @type Number
9692      */
9693     El.VISIBILITY = 1;
9694     /**
9695      * Visibility mode constant - Use display to hide element
9696      * @static
9697      * @type Number
9698      */
9699     El.DISPLAY = 2;
9700
9701     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9702     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9703     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9704
9705
9706
9707     /**
9708      * @private
9709      */
9710     El.cache = {};
9711
9712     var docEl;
9713
9714     /**
9715      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9716      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9717      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9718      * @return {Element} The Element object
9719      * @static
9720      */
9721     El.get = function(el){
9722         var ex, elm, id;
9723         if(!el){ return null; }
9724         if(typeof el == "string"){ // element id
9725             if(!(elm = document.getElementById(el))){
9726                 return null;
9727             }
9728             if(ex = El.cache[el]){
9729                 ex.dom = elm;
9730             }else{
9731                 ex = El.cache[el] = new El(elm);
9732             }
9733             return ex;
9734         }else if(el.tagName){ // dom element
9735             if(!(id = el.id)){
9736                 id = Roo.id(el);
9737             }
9738             if(ex = El.cache[id]){
9739                 ex.dom = el;
9740             }else{
9741                 ex = El.cache[id] = new El(el);
9742             }
9743             return ex;
9744         }else if(el instanceof El){
9745             if(el != docEl){
9746                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9747                                                               // catch case where it hasn't been appended
9748                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9749             }
9750             return el;
9751         }else if(el.isComposite){
9752             return el;
9753         }else if(el instanceof Array){
9754             return El.select(el);
9755         }else if(el == document){
9756             // create a bogus element object representing the document object
9757             if(!docEl){
9758                 var f = function(){};
9759                 f.prototype = El.prototype;
9760                 docEl = new f();
9761                 docEl.dom = document;
9762             }
9763             return docEl;
9764         }
9765         return null;
9766     };
9767
9768     // private
9769     El.uncache = function(el){
9770         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9771             if(a[i]){
9772                 delete El.cache[a[i].id || a[i]];
9773             }
9774         }
9775     };
9776
9777     // private
9778     // Garbage collection - uncache elements/purge listeners on orphaned elements
9779     // so we don't hold a reference and cause the browser to retain them
9780     El.garbageCollect = function(){
9781         if(!Roo.enableGarbageCollector){
9782             clearInterval(El.collectorThread);
9783             return;
9784         }
9785         for(var eid in El.cache){
9786             var el = El.cache[eid], d = el.dom;
9787             // -------------------------------------------------------
9788             // Determining what is garbage:
9789             // -------------------------------------------------------
9790             // !d
9791             // dom node is null, definitely garbage
9792             // -------------------------------------------------------
9793             // !d.parentNode
9794             // no parentNode == direct orphan, definitely garbage
9795             // -------------------------------------------------------
9796             // !d.offsetParent && !document.getElementById(eid)
9797             // display none elements have no offsetParent so we will
9798             // also try to look it up by it's id. However, check
9799             // offsetParent first so we don't do unneeded lookups.
9800             // This enables collection of elements that are not orphans
9801             // directly, but somewhere up the line they have an orphan
9802             // parent.
9803             // -------------------------------------------------------
9804             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9805                 delete El.cache[eid];
9806                 if(d && Roo.enableListenerCollection){
9807                     E.purgeElement(d);
9808                 }
9809             }
9810         }
9811     }
9812     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9813
9814
9815     // dom is optional
9816     El.Flyweight = function(dom){
9817         this.dom = dom;
9818     };
9819     El.Flyweight.prototype = El.prototype;
9820
9821     El._flyweights = {};
9822     /**
9823      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9824      * the dom node can be overwritten by other code.
9825      * @param {String/HTMLElement} el The dom node or id
9826      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9827      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9828      * @static
9829      * @return {Element} The shared Element object
9830      */
9831     El.fly = function(el, named){
9832         named = named || '_global';
9833         el = Roo.getDom(el);
9834         if(!el){
9835             return null;
9836         }
9837         if(!El._flyweights[named]){
9838             El._flyweights[named] = new El.Flyweight();
9839         }
9840         El._flyweights[named].dom = el;
9841         return El._flyweights[named];
9842     };
9843
9844     /**
9845      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9846      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9847      * Shorthand of {@link Roo.Element#get}
9848      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9849      * @return {Element} The Element object
9850      * @member Roo
9851      * @method get
9852      */
9853     Roo.get = El.get;
9854     /**
9855      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9856      * the dom node can be overwritten by other code.
9857      * Shorthand of {@link Roo.Element#fly}
9858      * @param {String/HTMLElement} el The dom node or id
9859      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9860      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9861      * @static
9862      * @return {Element} The shared Element object
9863      * @member Roo
9864      * @method fly
9865      */
9866     Roo.fly = El.fly;
9867
9868     // speedy lookup for elements never to box adjust
9869     var noBoxAdjust = Roo.isStrict ? {
9870         select:1
9871     } : {
9872         input:1, select:1, textarea:1
9873     };
9874     if(Roo.isIE || Roo.isGecko){
9875         noBoxAdjust['button'] = 1;
9876     }
9877
9878
9879     Roo.EventManager.on(window, 'unload', function(){
9880         delete El.cache;
9881         delete El._flyweights;
9882     });
9883 })();
9884
9885
9886
9887
9888 if(Roo.DomQuery){
9889     Roo.Element.selectorFunction = Roo.DomQuery.select;
9890 }
9891
9892 Roo.Element.select = function(selector, unique, root){
9893     var els;
9894     if(typeof selector == "string"){
9895         els = Roo.Element.selectorFunction(selector, root);
9896     }else if(selector.length !== undefined){
9897         els = selector;
9898     }else{
9899         throw "Invalid selector";
9900     }
9901     if(unique === true){
9902         return new Roo.CompositeElement(els);
9903     }else{
9904         return new Roo.CompositeElementLite(els);
9905     }
9906 };
9907 /**
9908  * Selects elements based on the passed CSS selector to enable working on them as 1.
9909  * @param {String/Array} selector The CSS selector or an array of elements
9910  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9911  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9912  * @return {CompositeElementLite/CompositeElement}
9913  * @member Roo
9914  * @method select
9915  */
9916 Roo.select = Roo.Element.select;
9917
9918
9919
9920
9921
9922
9923
9924
9925
9926
9927
9928
9929
9930
9931 /*
9932  * Based on:
9933  * Ext JS Library 1.1.1
9934  * Copyright(c) 2006-2007, Ext JS, LLC.
9935  *
9936  * Originally Released Under LGPL - original licence link has changed is not relivant.
9937  *
9938  * Fork - LGPL
9939  * <script type="text/javascript">
9940  */
9941
9942
9943
9944 //Notifies Element that fx methods are available
9945 Roo.enableFx = true;
9946
9947 /**
9948  * @class Roo.Fx
9949  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9950  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9951  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9952  * Element effects to work.</p><br/>
9953  *
9954  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9955  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9956  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9957  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9958  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9959  * expected results and should be done with care.</p><br/>
9960  *
9961  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9962  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9963 <pre>
9964 Value  Description
9965 -----  -----------------------------
9966 tl     The top left corner
9967 t      The center of the top edge
9968 tr     The top right corner
9969 l      The center of the left edge
9970 r      The center of the right edge
9971 bl     The bottom left corner
9972 b      The center of the bottom edge
9973 br     The bottom right corner
9974 </pre>
9975  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9976  * below are common options that can be passed to any Fx method.</b>
9977  * @cfg {Function} callback A function called when the effect is finished
9978  * @cfg {Object} scope The scope of the effect function
9979  * @cfg {String} easing A valid Easing value for the effect
9980  * @cfg {String} afterCls A css class to apply after the effect
9981  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9982  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9983  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9984  * effects that end with the element being visually hidden, ignored otherwise)
9985  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9986  * a function which returns such a specification that will be applied to the Element after the effect finishes
9987  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9988  * @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
9989  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9990  */
9991 Roo.Fx = {
9992         /**
9993          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9994          * origin for the slide effect.  This function automatically handles wrapping the element with
9995          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9996          * Usage:
9997          *<pre><code>
9998 // default: slide the element in from the top
9999 el.slideIn();
10000
10001 // custom: slide the element in from the right with a 2-second duration
10002 el.slideIn('r', { duration: 2 });
10003
10004 // common config options shown with default values
10005 el.slideIn('t', {
10006     easing: 'easeOut',
10007     duration: .5
10008 });
10009 </code></pre>
10010          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10011          * @param {Object} options (optional) Object literal with any of the Fx config options
10012          * @return {Roo.Element} The Element
10013          */
10014     slideIn : function(anchor, o){
10015         var el = this.getFxEl();
10016         o = o || {};
10017
10018         el.queueFx(o, function(){
10019
10020             anchor = anchor || "t";
10021
10022             // fix display to visibility
10023             this.fixDisplay();
10024
10025             // restore values after effect
10026             var r = this.getFxRestore();
10027             var b = this.getBox();
10028             // fixed size for slide
10029             this.setSize(b);
10030
10031             // wrap if needed
10032             var wrap = this.fxWrap(r.pos, o, "hidden");
10033
10034             var st = this.dom.style;
10035             st.visibility = "visible";
10036             st.position = "absolute";
10037
10038             // clear out temp styles after slide and unwrap
10039             var after = function(){
10040                 el.fxUnwrap(wrap, r.pos, o);
10041                 st.width = r.width;
10042                 st.height = r.height;
10043                 el.afterFx(o);
10044             };
10045             // time to calc the positions
10046             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10047
10048             switch(anchor.toLowerCase()){
10049                 case "t":
10050                     wrap.setSize(b.width, 0);
10051                     st.left = st.bottom = "0";
10052                     a = {height: bh};
10053                 break;
10054                 case "l":
10055                     wrap.setSize(0, b.height);
10056                     st.right = st.top = "0";
10057                     a = {width: bw};
10058                 break;
10059                 case "r":
10060                     wrap.setSize(0, b.height);
10061                     wrap.setX(b.right);
10062                     st.left = st.top = "0";
10063                     a = {width: bw, points: pt};
10064                 break;
10065                 case "b":
10066                     wrap.setSize(b.width, 0);
10067                     wrap.setY(b.bottom);
10068                     st.left = st.top = "0";
10069                     a = {height: bh, points: pt};
10070                 break;
10071                 case "tl":
10072                     wrap.setSize(0, 0);
10073                     st.right = st.bottom = "0";
10074                     a = {width: bw, height: bh};
10075                 break;
10076                 case "bl":
10077                     wrap.setSize(0, 0);
10078                     wrap.setY(b.y+b.height);
10079                     st.right = st.top = "0";
10080                     a = {width: bw, height: bh, points: pt};
10081                 break;
10082                 case "br":
10083                     wrap.setSize(0, 0);
10084                     wrap.setXY([b.right, b.bottom]);
10085                     st.left = st.top = "0";
10086                     a = {width: bw, height: bh, points: pt};
10087                 break;
10088                 case "tr":
10089                     wrap.setSize(0, 0);
10090                     wrap.setX(b.x+b.width);
10091                     st.left = st.bottom = "0";
10092                     a = {width: bw, height: bh, points: pt};
10093                 break;
10094             }
10095             this.dom.style.visibility = "visible";
10096             wrap.show();
10097
10098             arguments.callee.anim = wrap.fxanim(a,
10099                 o,
10100                 'motion',
10101                 .5,
10102                 'easeOut', after);
10103         });
10104         return this;
10105     },
10106     
10107         /**
10108          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10109          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10110          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10111          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10112          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10113          * Usage:
10114          *<pre><code>
10115 // default: slide the element out to the top
10116 el.slideOut();
10117
10118 // custom: slide the element out to the right with a 2-second duration
10119 el.slideOut('r', { duration: 2 });
10120
10121 // common config options shown with default values
10122 el.slideOut('t', {
10123     easing: 'easeOut',
10124     duration: .5,
10125     remove: false,
10126     useDisplay: false
10127 });
10128 </code></pre>
10129          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10130          * @param {Object} options (optional) Object literal with any of the Fx config options
10131          * @return {Roo.Element} The Element
10132          */
10133     slideOut : function(anchor, o){
10134         var el = this.getFxEl();
10135         o = o || {};
10136
10137         el.queueFx(o, function(){
10138
10139             anchor = anchor || "t";
10140
10141             // restore values after effect
10142             var r = this.getFxRestore();
10143             
10144             var b = this.getBox();
10145             // fixed size for slide
10146             this.setSize(b);
10147
10148             // wrap if needed
10149             var wrap = this.fxWrap(r.pos, o, "visible");
10150
10151             var st = this.dom.style;
10152             st.visibility = "visible";
10153             st.position = "absolute";
10154
10155             wrap.setSize(b);
10156
10157             var after = function(){
10158                 if(o.useDisplay){
10159                     el.setDisplayed(false);
10160                 }else{
10161                     el.hide();
10162                 }
10163
10164                 el.fxUnwrap(wrap, r.pos, o);
10165
10166                 st.width = r.width;
10167                 st.height = r.height;
10168
10169                 el.afterFx(o);
10170             };
10171
10172             var a, zero = {to: 0};
10173             switch(anchor.toLowerCase()){
10174                 case "t":
10175                     st.left = st.bottom = "0";
10176                     a = {height: zero};
10177                 break;
10178                 case "l":
10179                     st.right = st.top = "0";
10180                     a = {width: zero};
10181                 break;
10182                 case "r":
10183                     st.left = st.top = "0";
10184                     a = {width: zero, points: {to:[b.right, b.y]}};
10185                 break;
10186                 case "b":
10187                     st.left = st.top = "0";
10188                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10189                 break;
10190                 case "tl":
10191                     st.right = st.bottom = "0";
10192                     a = {width: zero, height: zero};
10193                 break;
10194                 case "bl":
10195                     st.right = st.top = "0";
10196                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10197                 break;
10198                 case "br":
10199                     st.left = st.top = "0";
10200                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10201                 break;
10202                 case "tr":
10203                     st.left = st.bottom = "0";
10204                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10205                 break;
10206             }
10207
10208             arguments.callee.anim = wrap.fxanim(a,
10209                 o,
10210                 'motion',
10211                 .5,
10212                 "easeOut", after);
10213         });
10214         return this;
10215     },
10216
10217         /**
10218          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10219          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10220          * The element must be removed from the DOM using the 'remove' config option if desired.
10221          * Usage:
10222          *<pre><code>
10223 // default
10224 el.puff();
10225
10226 // common config options shown with default values
10227 el.puff({
10228     easing: 'easeOut',
10229     duration: .5,
10230     remove: false,
10231     useDisplay: false
10232 });
10233 </code></pre>
10234          * @param {Object} options (optional) Object literal with any of the Fx config options
10235          * @return {Roo.Element} The Element
10236          */
10237     puff : function(o){
10238         var el = this.getFxEl();
10239         o = o || {};
10240
10241         el.queueFx(o, function(){
10242             this.clearOpacity();
10243             this.show();
10244
10245             // restore values after effect
10246             var r = this.getFxRestore();
10247             var st = this.dom.style;
10248
10249             var after = function(){
10250                 if(o.useDisplay){
10251                     el.setDisplayed(false);
10252                 }else{
10253                     el.hide();
10254                 }
10255
10256                 el.clearOpacity();
10257
10258                 el.setPositioning(r.pos);
10259                 st.width = r.width;
10260                 st.height = r.height;
10261                 st.fontSize = '';
10262                 el.afterFx(o);
10263             };
10264
10265             var width = this.getWidth();
10266             var height = this.getHeight();
10267
10268             arguments.callee.anim = this.fxanim({
10269                     width : {to: this.adjustWidth(width * 2)},
10270                     height : {to: this.adjustHeight(height * 2)},
10271                     points : {by: [-(width * .5), -(height * .5)]},
10272                     opacity : {to: 0},
10273                     fontSize: {to:200, unit: "%"}
10274                 },
10275                 o,
10276                 'motion',
10277                 .5,
10278                 "easeOut", after);
10279         });
10280         return this;
10281     },
10282
10283         /**
10284          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10285          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10286          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10287          * Usage:
10288          *<pre><code>
10289 // default
10290 el.switchOff();
10291
10292 // all config options shown with default values
10293 el.switchOff({
10294     easing: 'easeIn',
10295     duration: .3,
10296     remove: false,
10297     useDisplay: false
10298 });
10299 </code></pre>
10300          * @param {Object} options (optional) Object literal with any of the Fx config options
10301          * @return {Roo.Element} The Element
10302          */
10303     switchOff : function(o){
10304         var el = this.getFxEl();
10305         o = o || {};
10306
10307         el.queueFx(o, function(){
10308             this.clearOpacity();
10309             this.clip();
10310
10311             // restore values after effect
10312             var r = this.getFxRestore();
10313             var st = this.dom.style;
10314
10315             var after = function(){
10316                 if(o.useDisplay){
10317                     el.setDisplayed(false);
10318                 }else{
10319                     el.hide();
10320                 }
10321
10322                 el.clearOpacity();
10323                 el.setPositioning(r.pos);
10324                 st.width = r.width;
10325                 st.height = r.height;
10326
10327                 el.afterFx(o);
10328             };
10329
10330             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10331                 this.clearOpacity();
10332                 (function(){
10333                     this.fxanim({
10334                         height:{to:1},
10335                         points:{by:[0, this.getHeight() * .5]}
10336                     }, o, 'motion', 0.3, 'easeIn', after);
10337                 }).defer(100, this);
10338             });
10339         });
10340         return this;
10341     },
10342
10343     /**
10344      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10345      * changed using the "attr" config option) and then fading back to the original color. If no original
10346      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10347      * Usage:
10348 <pre><code>
10349 // default: highlight background to yellow
10350 el.highlight();
10351
10352 // custom: highlight foreground text to blue for 2 seconds
10353 el.highlight("0000ff", { attr: 'color', duration: 2 });
10354
10355 // common config options shown with default values
10356 el.highlight("ffff9c", {
10357     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10358     endColor: (current color) or "ffffff",
10359     easing: 'easeIn',
10360     duration: 1
10361 });
10362 </code></pre>
10363      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10364      * @param {Object} options (optional) Object literal with any of the Fx config options
10365      * @return {Roo.Element} The Element
10366      */ 
10367     highlight : function(color, o){
10368         var el = this.getFxEl();
10369         o = o || {};
10370
10371         el.queueFx(o, function(){
10372             color = color || "ffff9c";
10373             attr = o.attr || "backgroundColor";
10374
10375             this.clearOpacity();
10376             this.show();
10377
10378             var origColor = this.getColor(attr);
10379             var restoreColor = this.dom.style[attr];
10380             endColor = (o.endColor || origColor) || "ffffff";
10381
10382             var after = function(){
10383                 el.dom.style[attr] = restoreColor;
10384                 el.afterFx(o);
10385             };
10386
10387             var a = {};
10388             a[attr] = {from: color, to: endColor};
10389             arguments.callee.anim = this.fxanim(a,
10390                 o,
10391                 'color',
10392                 1,
10393                 'easeIn', after);
10394         });
10395         return this;
10396     },
10397
10398    /**
10399     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10400     * Usage:
10401 <pre><code>
10402 // default: a single light blue ripple
10403 el.frame();
10404
10405 // custom: 3 red ripples lasting 3 seconds total
10406 el.frame("ff0000", 3, { duration: 3 });
10407
10408 // common config options shown with default values
10409 el.frame("C3DAF9", 1, {
10410     duration: 1 //duration of entire animation (not each individual ripple)
10411     // Note: Easing is not configurable and will be ignored if included
10412 });
10413 </code></pre>
10414     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10415     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10416     * @param {Object} options (optional) Object literal with any of the Fx config options
10417     * @return {Roo.Element} The Element
10418     */
10419     frame : function(color, count, o){
10420         var el = this.getFxEl();
10421         o = o || {};
10422
10423         el.queueFx(o, function(){
10424             color = color || "#C3DAF9";
10425             if(color.length == 6){
10426                 color = "#" + color;
10427             }
10428             count = count || 1;
10429             duration = o.duration || 1;
10430             this.show();
10431
10432             var b = this.getBox();
10433             var animFn = function(){
10434                 var proxy = this.createProxy({
10435
10436                      style:{
10437                         visbility:"hidden",
10438                         position:"absolute",
10439                         "z-index":"35000", // yee haw
10440                         border:"0px solid " + color
10441                      }
10442                   });
10443                 var scale = Roo.isBorderBox ? 2 : 1;
10444                 proxy.animate({
10445                     top:{from:b.y, to:b.y - 20},
10446                     left:{from:b.x, to:b.x - 20},
10447                     borderWidth:{from:0, to:10},
10448                     opacity:{from:1, to:0},
10449                     height:{from:b.height, to:(b.height + (20*scale))},
10450                     width:{from:b.width, to:(b.width + (20*scale))}
10451                 }, duration, function(){
10452                     proxy.remove();
10453                 });
10454                 if(--count > 0){
10455                      animFn.defer((duration/2)*1000, this);
10456                 }else{
10457                     el.afterFx(o);
10458                 }
10459             };
10460             animFn.call(this);
10461         });
10462         return this;
10463     },
10464
10465    /**
10466     * Creates a pause before any subsequent queued effects begin.  If there are
10467     * no effects queued after the pause it will have no effect.
10468     * Usage:
10469 <pre><code>
10470 el.pause(1);
10471 </code></pre>
10472     * @param {Number} seconds The length of time to pause (in seconds)
10473     * @return {Roo.Element} The Element
10474     */
10475     pause : function(seconds){
10476         var el = this.getFxEl();
10477         var o = {};
10478
10479         el.queueFx(o, function(){
10480             setTimeout(function(){
10481                 el.afterFx(o);
10482             }, seconds * 1000);
10483         });
10484         return this;
10485     },
10486
10487    /**
10488     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10489     * using the "endOpacity" config option.
10490     * Usage:
10491 <pre><code>
10492 // default: fade in from opacity 0 to 100%
10493 el.fadeIn();
10494
10495 // custom: fade in from opacity 0 to 75% over 2 seconds
10496 el.fadeIn({ endOpacity: .75, duration: 2});
10497
10498 // common config options shown with default values
10499 el.fadeIn({
10500     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10501     easing: 'easeOut',
10502     duration: .5
10503 });
10504 </code></pre>
10505     * @param {Object} options (optional) Object literal with any of the Fx config options
10506     * @return {Roo.Element} The Element
10507     */
10508     fadeIn : function(o){
10509         var el = this.getFxEl();
10510         o = o || {};
10511         el.queueFx(o, function(){
10512             this.setOpacity(0);
10513             this.fixDisplay();
10514             this.dom.style.visibility = 'visible';
10515             var to = o.endOpacity || 1;
10516             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10517                 o, null, .5, "easeOut", function(){
10518                 if(to == 1){
10519                     this.clearOpacity();
10520                 }
10521                 el.afterFx(o);
10522             });
10523         });
10524         return this;
10525     },
10526
10527    /**
10528     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10529     * using the "endOpacity" config option.
10530     * Usage:
10531 <pre><code>
10532 // default: fade out from the element's current opacity to 0
10533 el.fadeOut();
10534
10535 // custom: fade out from the element's current opacity to 25% over 2 seconds
10536 el.fadeOut({ endOpacity: .25, duration: 2});
10537
10538 // common config options shown with default values
10539 el.fadeOut({
10540     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10541     easing: 'easeOut',
10542     duration: .5
10543     remove: false,
10544     useDisplay: false
10545 });
10546 </code></pre>
10547     * @param {Object} options (optional) Object literal with any of the Fx config options
10548     * @return {Roo.Element} The Element
10549     */
10550     fadeOut : function(o){
10551         var el = this.getFxEl();
10552         o = o || {};
10553         el.queueFx(o, function(){
10554             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10555                 o, null, .5, "easeOut", function(){
10556                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10557                      this.dom.style.display = "none";
10558                 }else{
10559                      this.dom.style.visibility = "hidden";
10560                 }
10561                 this.clearOpacity();
10562                 el.afterFx(o);
10563             });
10564         });
10565         return this;
10566     },
10567
10568    /**
10569     * Animates the transition of an element's dimensions from a starting height/width
10570     * to an ending height/width.
10571     * Usage:
10572 <pre><code>
10573 // change height and width to 100x100 pixels
10574 el.scale(100, 100);
10575
10576 // common config options shown with default values.  The height and width will default to
10577 // the element's existing values if passed as null.
10578 el.scale(
10579     [element's width],
10580     [element's height], {
10581     easing: 'easeOut',
10582     duration: .35
10583 });
10584 </code></pre>
10585     * @param {Number} width  The new width (pass undefined to keep the original width)
10586     * @param {Number} height  The new height (pass undefined to keep the original height)
10587     * @param {Object} options (optional) Object literal with any of the Fx config options
10588     * @return {Roo.Element} The Element
10589     */
10590     scale : function(w, h, o){
10591         this.shift(Roo.apply({}, o, {
10592             width: w,
10593             height: h
10594         }));
10595         return this;
10596     },
10597
10598    /**
10599     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10600     * Any of these properties not specified in the config object will not be changed.  This effect 
10601     * requires that at least one new dimension, position or opacity setting must be passed in on
10602     * the config object in order for the function to have any effect.
10603     * Usage:
10604 <pre><code>
10605 // slide the element horizontally to x position 200 while changing the height and opacity
10606 el.shift({ x: 200, height: 50, opacity: .8 });
10607
10608 // common config options shown with default values.
10609 el.shift({
10610     width: [element's width],
10611     height: [element's height],
10612     x: [element's x position],
10613     y: [element's y position],
10614     opacity: [element's opacity],
10615     easing: 'easeOut',
10616     duration: .35
10617 });
10618 </code></pre>
10619     * @param {Object} options  Object literal with any of the Fx config options
10620     * @return {Roo.Element} The Element
10621     */
10622     shift : function(o){
10623         var el = this.getFxEl();
10624         o = o || {};
10625         el.queueFx(o, function(){
10626             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10627             if(w !== undefined){
10628                 a.width = {to: this.adjustWidth(w)};
10629             }
10630             if(h !== undefined){
10631                 a.height = {to: this.adjustHeight(h)};
10632             }
10633             if(x !== undefined || y !== undefined){
10634                 a.points = {to: [
10635                     x !== undefined ? x : this.getX(),
10636                     y !== undefined ? y : this.getY()
10637                 ]};
10638             }
10639             if(op !== undefined){
10640                 a.opacity = {to: op};
10641             }
10642             if(o.xy !== undefined){
10643                 a.points = {to: o.xy};
10644             }
10645             arguments.callee.anim = this.fxanim(a,
10646                 o, 'motion', .35, "easeOut", function(){
10647                 el.afterFx(o);
10648             });
10649         });
10650         return this;
10651     },
10652
10653         /**
10654          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10655          * ending point of the effect.
10656          * Usage:
10657          *<pre><code>
10658 // default: slide the element downward while fading out
10659 el.ghost();
10660
10661 // custom: slide the element out to the right with a 2-second duration
10662 el.ghost('r', { duration: 2 });
10663
10664 // common config options shown with default values
10665 el.ghost('b', {
10666     easing: 'easeOut',
10667     duration: .5
10668     remove: false,
10669     useDisplay: false
10670 });
10671 </code></pre>
10672          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10673          * @param {Object} options (optional) Object literal with any of the Fx config options
10674          * @return {Roo.Element} The Element
10675          */
10676     ghost : function(anchor, o){
10677         var el = this.getFxEl();
10678         o = o || {};
10679
10680         el.queueFx(o, function(){
10681             anchor = anchor || "b";
10682
10683             // restore values after effect
10684             var r = this.getFxRestore();
10685             var w = this.getWidth(),
10686                 h = this.getHeight();
10687
10688             var st = this.dom.style;
10689
10690             var after = function(){
10691                 if(o.useDisplay){
10692                     el.setDisplayed(false);
10693                 }else{
10694                     el.hide();
10695                 }
10696
10697                 el.clearOpacity();
10698                 el.setPositioning(r.pos);
10699                 st.width = r.width;
10700                 st.height = r.height;
10701
10702                 el.afterFx(o);
10703             };
10704
10705             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10706             switch(anchor.toLowerCase()){
10707                 case "t":
10708                     pt.by = [0, -h];
10709                 break;
10710                 case "l":
10711                     pt.by = [-w, 0];
10712                 break;
10713                 case "r":
10714                     pt.by = [w, 0];
10715                 break;
10716                 case "b":
10717                     pt.by = [0, h];
10718                 break;
10719                 case "tl":
10720                     pt.by = [-w, -h];
10721                 break;
10722                 case "bl":
10723                     pt.by = [-w, h];
10724                 break;
10725                 case "br":
10726                     pt.by = [w, h];
10727                 break;
10728                 case "tr":
10729                     pt.by = [w, -h];
10730                 break;
10731             }
10732
10733             arguments.callee.anim = this.fxanim(a,
10734                 o,
10735                 'motion',
10736                 .5,
10737                 "easeOut", after);
10738         });
10739         return this;
10740     },
10741
10742         /**
10743          * Ensures that all effects queued after syncFx is called on the element are
10744          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10745          * @return {Roo.Element} The Element
10746          */
10747     syncFx : function(){
10748         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10749             block : false,
10750             concurrent : true,
10751             stopFx : false
10752         });
10753         return this;
10754     },
10755
10756         /**
10757          * Ensures that all effects queued after sequenceFx is called on the element are
10758          * run in sequence.  This is the opposite of {@link #syncFx}.
10759          * @return {Roo.Element} The Element
10760          */
10761     sequenceFx : function(){
10762         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10763             block : false,
10764             concurrent : false,
10765             stopFx : false
10766         });
10767         return this;
10768     },
10769
10770         /* @private */
10771     nextFx : function(){
10772         var ef = this.fxQueue[0];
10773         if(ef){
10774             ef.call(this);
10775         }
10776     },
10777
10778         /**
10779          * Returns true if the element has any effects actively running or queued, else returns false.
10780          * @return {Boolean} True if element has active effects, else false
10781          */
10782     hasActiveFx : function(){
10783         return this.fxQueue && this.fxQueue[0];
10784     },
10785
10786         /**
10787          * Stops any running effects and clears the element's internal effects queue if it contains
10788          * any additional effects that haven't started yet.
10789          * @return {Roo.Element} The Element
10790          */
10791     stopFx : function(){
10792         if(this.hasActiveFx()){
10793             var cur = this.fxQueue[0];
10794             if(cur && cur.anim && cur.anim.isAnimated()){
10795                 this.fxQueue = [cur]; // clear out others
10796                 cur.anim.stop(true);
10797             }
10798         }
10799         return this;
10800     },
10801
10802         /* @private */
10803     beforeFx : function(o){
10804         if(this.hasActiveFx() && !o.concurrent){
10805            if(o.stopFx){
10806                this.stopFx();
10807                return true;
10808            }
10809            return false;
10810         }
10811         return true;
10812     },
10813
10814         /**
10815          * Returns true if the element is currently blocking so that no other effect can be queued
10816          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10817          * used to ensure that an effect initiated by a user action runs to completion prior to the
10818          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10819          * @return {Boolean} True if blocking, else false
10820          */
10821     hasFxBlock : function(){
10822         var q = this.fxQueue;
10823         return q && q[0] && q[0].block;
10824     },
10825
10826         /* @private */
10827     queueFx : function(o, fn){
10828         if(!this.fxQueue){
10829             this.fxQueue = [];
10830         }
10831         if(!this.hasFxBlock()){
10832             Roo.applyIf(o, this.fxDefaults);
10833             if(!o.concurrent){
10834                 var run = this.beforeFx(o);
10835                 fn.block = o.block;
10836                 this.fxQueue.push(fn);
10837                 if(run){
10838                     this.nextFx();
10839                 }
10840             }else{
10841                 fn.call(this);
10842             }
10843         }
10844         return this;
10845     },
10846
10847         /* @private */
10848     fxWrap : function(pos, o, vis){
10849         var wrap;
10850         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10851             var wrapXY;
10852             if(o.fixPosition){
10853                 wrapXY = this.getXY();
10854             }
10855             var div = document.createElement("div");
10856             div.style.visibility = vis;
10857             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10858             wrap.setPositioning(pos);
10859             if(wrap.getStyle("position") == "static"){
10860                 wrap.position("relative");
10861             }
10862             this.clearPositioning('auto');
10863             wrap.clip();
10864             wrap.dom.appendChild(this.dom);
10865             if(wrapXY){
10866                 wrap.setXY(wrapXY);
10867             }
10868         }
10869         return wrap;
10870     },
10871
10872         /* @private */
10873     fxUnwrap : function(wrap, pos, o){
10874         this.clearPositioning();
10875         this.setPositioning(pos);
10876         if(!o.wrap){
10877             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10878             wrap.remove();
10879         }
10880     },
10881
10882         /* @private */
10883     getFxRestore : function(){
10884         var st = this.dom.style;
10885         return {pos: this.getPositioning(), width: st.width, height : st.height};
10886     },
10887
10888         /* @private */
10889     afterFx : function(o){
10890         if(o.afterStyle){
10891             this.applyStyles(o.afterStyle);
10892         }
10893         if(o.afterCls){
10894             this.addClass(o.afterCls);
10895         }
10896         if(o.remove === true){
10897             this.remove();
10898         }
10899         Roo.callback(o.callback, o.scope, [this]);
10900         if(!o.concurrent){
10901             this.fxQueue.shift();
10902             this.nextFx();
10903         }
10904     },
10905
10906         /* @private */
10907     getFxEl : function(){ // support for composite element fx
10908         return Roo.get(this.dom);
10909     },
10910
10911         /* @private */
10912     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10913         animType = animType || 'run';
10914         opt = opt || {};
10915         var anim = Roo.lib.Anim[animType](
10916             this.dom, args,
10917             (opt.duration || defaultDur) || .35,
10918             (opt.easing || defaultEase) || 'easeOut',
10919             function(){
10920                 Roo.callback(cb, this);
10921             },
10922             this
10923         );
10924         opt.anim = anim;
10925         return anim;
10926     }
10927 };
10928
10929 // backwords compat
10930 Roo.Fx.resize = Roo.Fx.scale;
10931
10932 //When included, Roo.Fx is automatically applied to Element so that all basic
10933 //effects are available directly via the Element API
10934 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10935  * Based on:
10936  * Ext JS Library 1.1.1
10937  * Copyright(c) 2006-2007, Ext JS, LLC.
10938  *
10939  * Originally Released Under LGPL - original licence link has changed is not relivant.
10940  *
10941  * Fork - LGPL
10942  * <script type="text/javascript">
10943  */
10944
10945
10946 /**
10947  * @class Roo.CompositeElement
10948  * Standard composite class. Creates a Roo.Element for every element in the collection.
10949  * <br><br>
10950  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10951  * actions will be performed on all the elements in this collection.</b>
10952  * <br><br>
10953  * All methods return <i>this</i> and can be chained.
10954  <pre><code>
10955  var els = Roo.select("#some-el div.some-class", true);
10956  // or select directly from an existing element
10957  var el = Roo.get('some-el');
10958  el.select('div.some-class', true);
10959
10960  els.setWidth(100); // all elements become 100 width
10961  els.hide(true); // all elements fade out and hide
10962  // or
10963  els.setWidth(100).hide(true);
10964  </code></pre>
10965  */
10966 Roo.CompositeElement = function(els){
10967     this.elements = [];
10968     this.addElements(els);
10969 };
10970 Roo.CompositeElement.prototype = {
10971     isComposite: true,
10972     addElements : function(els){
10973         if(!els) return this;
10974         if(typeof els == "string"){
10975             els = Roo.Element.selectorFunction(els);
10976         }
10977         var yels = this.elements;
10978         var index = yels.length-1;
10979         for(var i = 0, len = els.length; i < len; i++) {
10980                 yels[++index] = Roo.get(els[i]);
10981         }
10982         return this;
10983     },
10984
10985     /**
10986     * Clears this composite and adds the elements returned by the passed selector.
10987     * @param {String/Array} els A string CSS selector, an array of elements or an element
10988     * @return {CompositeElement} this
10989     */
10990     fill : function(els){
10991         this.elements = [];
10992         this.add(els);
10993         return this;
10994     },
10995
10996     /**
10997     * Filters this composite to only elements that match the passed selector.
10998     * @param {String} selector A string CSS selector
10999     * @param {Boolean} inverse return inverse filter (not matches)
11000     * @return {CompositeElement} this
11001     */
11002     filter : function(selector, inverse){
11003         var els = [];
11004         inverse = inverse || false;
11005         this.each(function(el){
11006             var match = inverse ? !el.is(selector) : el.is(selector);
11007             if(match){
11008                 els[els.length] = el.dom;
11009             }
11010         });
11011         this.fill(els);
11012         return this;
11013     },
11014
11015     invoke : function(fn, args){
11016         var els = this.elements;
11017         for(var i = 0, len = els.length; i < len; i++) {
11018                 Roo.Element.prototype[fn].apply(els[i], args);
11019         }
11020         return this;
11021     },
11022     /**
11023     * Adds elements to this composite.
11024     * @param {String/Array} els A string CSS selector, an array of elements or an element
11025     * @return {CompositeElement} this
11026     */
11027     add : function(els){
11028         if(typeof els == "string"){
11029             this.addElements(Roo.Element.selectorFunction(els));
11030         }else if(els.length !== undefined){
11031             this.addElements(els);
11032         }else{
11033             this.addElements([els]);
11034         }
11035         return this;
11036     },
11037     /**
11038     * Calls the passed function passing (el, this, index) for each element in this composite.
11039     * @param {Function} fn The function to call
11040     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11041     * @return {CompositeElement} this
11042     */
11043     each : function(fn, scope){
11044         var els = this.elements;
11045         for(var i = 0, len = els.length; i < len; i++){
11046             if(fn.call(scope || els[i], els[i], this, i) === false) {
11047                 break;
11048             }
11049         }
11050         return this;
11051     },
11052
11053     /**
11054      * Returns the Element object at the specified index
11055      * @param {Number} index
11056      * @return {Roo.Element}
11057      */
11058     item : function(index){
11059         return this.elements[index] || null;
11060     },
11061
11062     /**
11063      * Returns the first Element
11064      * @return {Roo.Element}
11065      */
11066     first : function(){
11067         return this.item(0);
11068     },
11069
11070     /**
11071      * Returns the last Element
11072      * @return {Roo.Element}
11073      */
11074     last : function(){
11075         return this.item(this.elements.length-1);
11076     },
11077
11078     /**
11079      * Returns the number of elements in this composite
11080      * @return Number
11081      */
11082     getCount : function(){
11083         return this.elements.length;
11084     },
11085
11086     /**
11087      * Returns true if this composite contains the passed element
11088      * @return Boolean
11089      */
11090     contains : function(el){
11091         return this.indexOf(el) !== -1;
11092     },
11093
11094     /**
11095      * Returns true if this composite contains the passed element
11096      * @return Boolean
11097      */
11098     indexOf : function(el){
11099         return this.elements.indexOf(Roo.get(el));
11100     },
11101
11102
11103     /**
11104     * Removes the specified element(s).
11105     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11106     * or an array of any of those.
11107     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11108     * @return {CompositeElement} this
11109     */
11110     removeElement : function(el, removeDom){
11111         if(el instanceof Array){
11112             for(var i = 0, len = el.length; i < len; i++){
11113                 this.removeElement(el[i]);
11114             }
11115             return this;
11116         }
11117         var index = typeof el == 'number' ? el : this.indexOf(el);
11118         if(index !== -1){
11119             if(removeDom){
11120                 var d = this.elements[index];
11121                 if(d.dom){
11122                     d.remove();
11123                 }else{
11124                     d.parentNode.removeChild(d);
11125                 }
11126             }
11127             this.elements.splice(index, 1);
11128         }
11129         return this;
11130     },
11131
11132     /**
11133     * Replaces the specified element with the passed element.
11134     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11135     * to replace.
11136     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11137     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11138     * @return {CompositeElement} this
11139     */
11140     replaceElement : function(el, replacement, domReplace){
11141         var index = typeof el == 'number' ? el : this.indexOf(el);
11142         if(index !== -1){
11143             if(domReplace){
11144                 this.elements[index].replaceWith(replacement);
11145             }else{
11146                 this.elements.splice(index, 1, Roo.get(replacement))
11147             }
11148         }
11149         return this;
11150     },
11151
11152     /**
11153      * Removes all elements.
11154      */
11155     clear : function(){
11156         this.elements = [];
11157     }
11158 };
11159 (function(){
11160     Roo.CompositeElement.createCall = function(proto, fnName){
11161         if(!proto[fnName]){
11162             proto[fnName] = function(){
11163                 return this.invoke(fnName, arguments);
11164             };
11165         }
11166     };
11167     for(var fnName in Roo.Element.prototype){
11168         if(typeof Roo.Element.prototype[fnName] == "function"){
11169             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11170         }
11171     };
11172 })();
11173 /*
11174  * Based on:
11175  * Ext JS Library 1.1.1
11176  * Copyright(c) 2006-2007, Ext JS, LLC.
11177  *
11178  * Originally Released Under LGPL - original licence link has changed is not relivant.
11179  *
11180  * Fork - LGPL
11181  * <script type="text/javascript">
11182  */
11183
11184 /**
11185  * @class Roo.CompositeElementLite
11186  * @extends Roo.CompositeElement
11187  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11188  <pre><code>
11189  var els = Roo.select("#some-el div.some-class");
11190  // or select directly from an existing element
11191  var el = Roo.get('some-el');
11192  el.select('div.some-class');
11193
11194  els.setWidth(100); // all elements become 100 width
11195  els.hide(true); // all elements fade out and hide
11196  // or
11197  els.setWidth(100).hide(true);
11198  </code></pre><br><br>
11199  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11200  * actions will be performed on all the elements in this collection.</b>
11201  */
11202 Roo.CompositeElementLite = function(els){
11203     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11204     this.el = new Roo.Element.Flyweight();
11205 };
11206 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11207     addElements : function(els){
11208         if(els){
11209             if(els instanceof Array){
11210                 this.elements = this.elements.concat(els);
11211             }else{
11212                 var yels = this.elements;
11213                 var index = yels.length-1;
11214                 for(var i = 0, len = els.length; i < len; i++) {
11215                     yels[++index] = els[i];
11216                 }
11217             }
11218         }
11219         return this;
11220     },
11221     invoke : function(fn, args){
11222         var els = this.elements;
11223         var el = this.el;
11224         for(var i = 0, len = els.length; i < len; i++) {
11225             el.dom = els[i];
11226                 Roo.Element.prototype[fn].apply(el, args);
11227         }
11228         return this;
11229     },
11230     /**
11231      * Returns a flyweight Element of the dom element object at the specified index
11232      * @param {Number} index
11233      * @return {Roo.Element}
11234      */
11235     item : function(index){
11236         if(!this.elements[index]){
11237             return null;
11238         }
11239         this.el.dom = this.elements[index];
11240         return this.el;
11241     },
11242
11243     // fixes scope with flyweight
11244     addListener : function(eventName, handler, scope, opt){
11245         var els = this.elements;
11246         for(var i = 0, len = els.length; i < len; i++) {
11247             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11248         }
11249         return this;
11250     },
11251
11252     /**
11253     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11254     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11255     * a reference to the dom node, use el.dom.</b>
11256     * @param {Function} fn The function to call
11257     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11258     * @return {CompositeElement} this
11259     */
11260     each : function(fn, scope){
11261         var els = this.elements;
11262         var el = this.el;
11263         for(var i = 0, len = els.length; i < len; i++){
11264             el.dom = els[i];
11265                 if(fn.call(scope || el, el, this, i) === false){
11266                 break;
11267             }
11268         }
11269         return this;
11270     },
11271
11272     indexOf : function(el){
11273         return this.elements.indexOf(Roo.getDom(el));
11274     },
11275
11276     replaceElement : function(el, replacement, domReplace){
11277         var index = typeof el == 'number' ? el : this.indexOf(el);
11278         if(index !== -1){
11279             replacement = Roo.getDom(replacement);
11280             if(domReplace){
11281                 var d = this.elements[index];
11282                 d.parentNode.insertBefore(replacement, d);
11283                 d.parentNode.removeChild(d);
11284             }
11285             this.elements.splice(index, 1, replacement);
11286         }
11287         return this;
11288     }
11289 });
11290 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11291
11292 /*
11293  * Based on:
11294  * Ext JS Library 1.1.1
11295  * Copyright(c) 2006-2007, Ext JS, LLC.
11296  *
11297  * Originally Released Under LGPL - original licence link has changed is not relivant.
11298  *
11299  * Fork - LGPL
11300  * <script type="text/javascript">
11301  */
11302
11303  
11304
11305 /**
11306  * @class Roo.data.Connection
11307  * @extends Roo.util.Observable
11308  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11309  * either to a configured URL, or to a URL specified at request time.<br><br>
11310  * <p>
11311  * Requests made by this class are asynchronous, and will return immediately. No data from
11312  * the server will be available to the statement immediately following the {@link #request} call.
11313  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11314  * <p>
11315  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11316  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11317  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11318  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11319  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11320  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11321  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11322  * standard DOM methods.
11323  * @constructor
11324  * @param {Object} config a configuration object.
11325  */
11326 Roo.data.Connection = function(config){
11327     Roo.apply(this, config);
11328     this.addEvents({
11329         /**
11330          * @event beforerequest
11331          * Fires before a network request is made to retrieve a data object.
11332          * @param {Connection} conn This Connection object.
11333          * @param {Object} options The options config object passed to the {@link #request} method.
11334          */
11335         "beforerequest" : true,
11336         /**
11337          * @event requestcomplete
11338          * Fires if the request was successfully completed.
11339          * @param {Connection} conn This Connection object.
11340          * @param {Object} response The XHR object containing the response data.
11341          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11342          * @param {Object} options The options config object passed to the {@link #request} method.
11343          */
11344         "requestcomplete" : true,
11345         /**
11346          * @event requestexception
11347          * Fires if an error HTTP status was returned from the server.
11348          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11349          * @param {Connection} conn This Connection object.
11350          * @param {Object} response The XHR object containing the response data.
11351          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11352          * @param {Object} options The options config object passed to the {@link #request} method.
11353          */
11354         "requestexception" : true
11355     });
11356     Roo.data.Connection.superclass.constructor.call(this);
11357 };
11358
11359 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11360     /**
11361      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11362      */
11363     /**
11364      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11365      * extra parameters to each request made by this object. (defaults to undefined)
11366      */
11367     /**
11368      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11369      *  to each request made by this object. (defaults to undefined)
11370      */
11371     /**
11372      * @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)
11373      */
11374     /**
11375      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11376      */
11377     timeout : 30000,
11378     /**
11379      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11380      * @type Boolean
11381      */
11382     autoAbort:false,
11383
11384     /**
11385      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11386      * @type Boolean
11387      */
11388     disableCaching: true,
11389
11390     /**
11391      * Sends an HTTP request to a remote server.
11392      * @param {Object} options An object which may contain the following properties:<ul>
11393      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11394      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11395      * request, a url encoded string or a function to call to get either.</li>
11396      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11397      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11398      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11399      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11400      * <li>options {Object} The parameter to the request call.</li>
11401      * <li>success {Boolean} True if the request succeeded.</li>
11402      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11403      * </ul></li>
11404      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11405      * The callback is passed the following parameters:<ul>
11406      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11407      * <li>options {Object} The parameter to the request call.</li>
11408      * </ul></li>
11409      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11410      * The callback is passed the following parameters:<ul>
11411      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11412      * <li>options {Object} The parameter to the request call.</li>
11413      * </ul></li>
11414      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11415      * for the callback function. Defaults to the browser window.</li>
11416      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11417      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11418      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11419      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11420      * params for the post data. Any params will be appended to the URL.</li>
11421      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11422      * </ul>
11423      * @return {Number} transactionId
11424      */
11425     request : function(o){
11426         if(this.fireEvent("beforerequest", this, o) !== false){
11427             var p = o.params;
11428
11429             if(typeof p == "function"){
11430                 p = p.call(o.scope||window, o);
11431             }
11432             if(typeof p == "object"){
11433                 p = Roo.urlEncode(o.params);
11434             }
11435             if(this.extraParams){
11436                 var extras = Roo.urlEncode(this.extraParams);
11437                 p = p ? (p + '&' + extras) : extras;
11438             }
11439
11440             var url = o.url || this.url;
11441             if(typeof url == 'function'){
11442                 url = url.call(o.scope||window, o);
11443             }
11444
11445             if(o.form){
11446                 var form = Roo.getDom(o.form);
11447                 url = url || form.action;
11448
11449                 var enctype = form.getAttribute("enctype");
11450                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11451                     return this.doFormUpload(o, p, url);
11452                 }
11453                 var f = Roo.lib.Ajax.serializeForm(form);
11454                 p = p ? (p + '&' + f) : f;
11455             }
11456
11457             var hs = o.headers;
11458             if(this.defaultHeaders){
11459                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11460                 if(!o.headers){
11461                     o.headers = hs;
11462                 }
11463             }
11464
11465             var cb = {
11466                 success: this.handleResponse,
11467                 failure: this.handleFailure,
11468                 scope: this,
11469                 argument: {options: o},
11470                 timeout : o.timeout || this.timeout
11471             };
11472
11473             var method = o.method||this.method||(p ? "POST" : "GET");
11474
11475             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11476                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11477             }
11478
11479             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11480                 if(o.autoAbort){
11481                     this.abort();
11482                 }
11483             }else if(this.autoAbort !== false){
11484                 this.abort();
11485             }
11486
11487             if((method == 'GET' && p) || o.xmlData){
11488                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11489                 p = '';
11490             }
11491             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11492             return this.transId;
11493         }else{
11494             Roo.callback(o.callback, o.scope, [o, null, null]);
11495             return null;
11496         }
11497     },
11498
11499     /**
11500      * Determine whether this object has a request outstanding.
11501      * @param {Number} transactionId (Optional) defaults to the last transaction
11502      * @return {Boolean} True if there is an outstanding request.
11503      */
11504     isLoading : function(transId){
11505         if(transId){
11506             return Roo.lib.Ajax.isCallInProgress(transId);
11507         }else{
11508             return this.transId ? true : false;
11509         }
11510     },
11511
11512     /**
11513      * Aborts any outstanding request.
11514      * @param {Number} transactionId (Optional) defaults to the last transaction
11515      */
11516     abort : function(transId){
11517         if(transId || this.isLoading()){
11518             Roo.lib.Ajax.abort(transId || this.transId);
11519         }
11520     },
11521
11522     // private
11523     handleResponse : function(response){
11524         this.transId = false;
11525         var options = response.argument.options;
11526         response.argument = options ? options.argument : null;
11527         this.fireEvent("requestcomplete", this, response, options);
11528         Roo.callback(options.success, options.scope, [response, options]);
11529         Roo.callback(options.callback, options.scope, [options, true, response]);
11530     },
11531
11532     // private
11533     handleFailure : function(response, e){
11534         this.transId = false;
11535         var options = response.argument.options;
11536         response.argument = options ? options.argument : null;
11537         this.fireEvent("requestexception", this, response, options, e);
11538         Roo.callback(options.failure, options.scope, [response, options]);
11539         Roo.callback(options.callback, options.scope, [options, false, response]);
11540     },
11541
11542     // private
11543     doFormUpload : function(o, ps, url){
11544         var id = Roo.id();
11545         var frame = document.createElement('iframe');
11546         frame.id = id;
11547         frame.name = id;
11548         frame.className = 'x-hidden';
11549         if(Roo.isIE){
11550             frame.src = Roo.SSL_SECURE_URL;
11551         }
11552         document.body.appendChild(frame);
11553
11554         if(Roo.isIE){
11555            document.frames[id].name = id;
11556         }
11557
11558         var form = Roo.getDom(o.form);
11559         form.target = id;
11560         form.method = 'POST';
11561         form.enctype = form.encoding = 'multipart/form-data';
11562         if(url){
11563             form.action = url;
11564         }
11565
11566         var hiddens, hd;
11567         if(ps){ // add dynamic params
11568             hiddens = [];
11569             ps = Roo.urlDecode(ps, false);
11570             for(var k in ps){
11571                 if(ps.hasOwnProperty(k)){
11572                     hd = document.createElement('input');
11573                     hd.type = 'hidden';
11574                     hd.name = k;
11575                     hd.value = ps[k];
11576                     form.appendChild(hd);
11577                     hiddens.push(hd);
11578                 }
11579             }
11580         }
11581
11582         function cb(){
11583             var r = {  // bogus response object
11584                 responseText : '',
11585                 responseXML : null
11586             };
11587
11588             r.argument = o ? o.argument : null;
11589
11590             try { //
11591                 var doc;
11592                 if(Roo.isIE){
11593                     doc = frame.contentWindow.document;
11594                 }else {
11595                     doc = (frame.contentDocument || window.frames[id].document);
11596                 }
11597                 if(doc && doc.body){
11598                     r.responseText = doc.body.innerHTML;
11599                 }
11600                 if(doc && doc.XMLDocument){
11601                     r.responseXML = doc.XMLDocument;
11602                 }else {
11603                     r.responseXML = doc;
11604                 }
11605             }
11606             catch(e) {
11607                 // ignore
11608             }
11609
11610             Roo.EventManager.removeListener(frame, 'load', cb, this);
11611
11612             this.fireEvent("requestcomplete", this, r, o);
11613             Roo.callback(o.success, o.scope, [r, o]);
11614             Roo.callback(o.callback, o.scope, [o, true, r]);
11615
11616             setTimeout(function(){document.body.removeChild(frame);}, 100);
11617         }
11618
11619         Roo.EventManager.on(frame, 'load', cb, this);
11620         form.submit();
11621
11622         if(hiddens){ // remove dynamic params
11623             for(var i = 0, len = hiddens.length; i < len; i++){
11624                 form.removeChild(hiddens[i]);
11625             }
11626         }
11627     }
11628 });
11629 /*
11630  * Based on:
11631  * Ext JS Library 1.1.1
11632  * Copyright(c) 2006-2007, Ext JS, LLC.
11633  *
11634  * Originally Released Under LGPL - original licence link has changed is not relivant.
11635  *
11636  * Fork - LGPL
11637  * <script type="text/javascript">
11638  */
11639  
11640 /**
11641  * Global Ajax request class.
11642  * 
11643  * @class Roo.Ajax
11644  * @extends Roo.data.Connection
11645  * @static
11646  * 
11647  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11648  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11649  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11650  * @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)
11651  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11652  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11653  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11654  */
11655 Roo.Ajax = new Roo.data.Connection({
11656     // fix up the docs
11657     /**
11658      * @scope Roo.Ajax
11659      * @type {Boolear} 
11660      */
11661     autoAbort : false,
11662
11663     /**
11664      * Serialize the passed form into a url encoded string
11665      * @scope Roo.Ajax
11666      * @param {String/HTMLElement} form
11667      * @return {String}
11668      */
11669     serializeForm : function(form){
11670         return Roo.lib.Ajax.serializeForm(form);
11671     }
11672 });/*
11673  * Based on:
11674  * Ext JS Library 1.1.1
11675  * Copyright(c) 2006-2007, Ext JS, LLC.
11676  *
11677  * Originally Released Under LGPL - original licence link has changed is not relivant.
11678  *
11679  * Fork - LGPL
11680  * <script type="text/javascript">
11681  */
11682
11683  
11684 /**
11685  * @class Roo.UpdateManager
11686  * @extends Roo.util.Observable
11687  * Provides AJAX-style update for Element object.<br><br>
11688  * Usage:<br>
11689  * <pre><code>
11690  * // Get it from a Roo.Element object
11691  * var el = Roo.get("foo");
11692  * var mgr = el.getUpdateManager();
11693  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11694  * ...
11695  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11696  * <br>
11697  * // or directly (returns the same UpdateManager instance)
11698  * var mgr = new Roo.UpdateManager("myElementId");
11699  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11700  * mgr.on("update", myFcnNeedsToKnow);
11701  * <br>
11702    // short handed call directly from the element object
11703    Roo.get("foo").load({
11704         url: "bar.php",
11705         scripts:true,
11706         params: "for=bar",
11707         text: "Loading Foo..."
11708    });
11709  * </code></pre>
11710  * @constructor
11711  * Create new UpdateManager directly.
11712  * @param {String/HTMLElement/Roo.Element} el The element to update
11713  * @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).
11714  */
11715 Roo.UpdateManager = function(el, forceNew){
11716     el = Roo.get(el);
11717     if(!forceNew && el.updateManager){
11718         return el.updateManager;
11719     }
11720     /**
11721      * The Element object
11722      * @type Roo.Element
11723      */
11724     this.el = el;
11725     /**
11726      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11727      * @type String
11728      */
11729     this.defaultUrl = null;
11730
11731     this.addEvents({
11732         /**
11733          * @event beforeupdate
11734          * Fired before an update is made, return false from your handler and the update is cancelled.
11735          * @param {Roo.Element} el
11736          * @param {String/Object/Function} url
11737          * @param {String/Object} params
11738          */
11739         "beforeupdate": true,
11740         /**
11741          * @event update
11742          * Fired after successful update is made.
11743          * @param {Roo.Element} el
11744          * @param {Object} oResponseObject The response Object
11745          */
11746         "update": true,
11747         /**
11748          * @event failure
11749          * Fired on update failure.
11750          * @param {Roo.Element} el
11751          * @param {Object} oResponseObject The response Object
11752          */
11753         "failure": true
11754     });
11755     var d = Roo.UpdateManager.defaults;
11756     /**
11757      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11758      * @type String
11759      */
11760     this.sslBlankUrl = d.sslBlankUrl;
11761     /**
11762      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11763      * @type Boolean
11764      */
11765     this.disableCaching = d.disableCaching;
11766     /**
11767      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11768      * @type String
11769      */
11770     this.indicatorText = d.indicatorText;
11771     /**
11772      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11773      * @type String
11774      */
11775     this.showLoadIndicator = d.showLoadIndicator;
11776     /**
11777      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11778      * @type Number
11779      */
11780     this.timeout = d.timeout;
11781
11782     /**
11783      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11784      * @type Boolean
11785      */
11786     this.loadScripts = d.loadScripts;
11787
11788     /**
11789      * Transaction object of current executing transaction
11790      */
11791     this.transaction = null;
11792
11793     /**
11794      * @private
11795      */
11796     this.autoRefreshProcId = null;
11797     /**
11798      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11799      * @type Function
11800      */
11801     this.refreshDelegate = this.refresh.createDelegate(this);
11802     /**
11803      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11804      * @type Function
11805      */
11806     this.updateDelegate = this.update.createDelegate(this);
11807     /**
11808      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11809      * @type Function
11810      */
11811     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11812     /**
11813      * @private
11814      */
11815     this.successDelegate = this.processSuccess.createDelegate(this);
11816     /**
11817      * @private
11818      */
11819     this.failureDelegate = this.processFailure.createDelegate(this);
11820
11821     if(!this.renderer){
11822      /**
11823       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11824       */
11825     this.renderer = new Roo.UpdateManager.BasicRenderer();
11826     }
11827     
11828     Roo.UpdateManager.superclass.constructor.call(this);
11829 };
11830
11831 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11832     /**
11833      * Get the Element this UpdateManager is bound to
11834      * @return {Roo.Element} The element
11835      */
11836     getEl : function(){
11837         return this.el;
11838     },
11839     /**
11840      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11841      * @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:
11842 <pre><code>
11843 um.update({<br/>
11844     url: "your-url.php",<br/>
11845     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11846     callback: yourFunction,<br/>
11847     scope: yourObject, //(optional scope)  <br/>
11848     discardUrl: false, <br/>
11849     nocache: false,<br/>
11850     text: "Loading...",<br/>
11851     timeout: 30,<br/>
11852     scripts: false<br/>
11853 });
11854 </code></pre>
11855      * The only required property is url. The optional properties nocache, text and scripts
11856      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11857      * @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}
11858      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11859      * @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.
11860      */
11861     update : function(url, params, callback, discardUrl){
11862         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11863             var method = this.method,
11864                 cfg;
11865             if(typeof url == "object"){ // must be config object
11866                 cfg = url;
11867                 url = cfg.url;
11868                 params = params || cfg.params;
11869                 callback = callback || cfg.callback;
11870                 discardUrl = discardUrl || cfg.discardUrl;
11871                 if(callback && cfg.scope){
11872                     callback = callback.createDelegate(cfg.scope);
11873                 }
11874                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11875                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11876                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11877                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11878                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11879             }
11880             this.showLoading();
11881             if(!discardUrl){
11882                 this.defaultUrl = url;
11883             }
11884             if(typeof url == "function"){
11885                 url = url.call(this);
11886             }
11887
11888             method = method || (params ? "POST" : "GET");
11889             if(method == "GET"){
11890                 url = this.prepareUrl(url);
11891             }
11892
11893             var o = Roo.apply(cfg ||{}, {
11894                 url : url,
11895                 params: params,
11896                 success: this.successDelegate,
11897                 failure: this.failureDelegate,
11898                 callback: undefined,
11899                 timeout: (this.timeout*1000),
11900                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11901             });
11902             Roo.log("updated manager called with timeout of " + o.timeout);
11903             this.transaction = Roo.Ajax.request(o);
11904         }
11905     },
11906
11907     /**
11908      * 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.
11909      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11910      * @param {String/HTMLElement} form The form Id or form element
11911      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11912      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11913      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11914      */
11915     formUpdate : function(form, url, reset, callback){
11916         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11917             if(typeof url == "function"){
11918                 url = url.call(this);
11919             }
11920             form = Roo.getDom(form);
11921             this.transaction = Roo.Ajax.request({
11922                 form: form,
11923                 url:url,
11924                 success: this.successDelegate,
11925                 failure: this.failureDelegate,
11926                 timeout: (this.timeout*1000),
11927                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11928             });
11929             this.showLoading.defer(1, this);
11930         }
11931     },
11932
11933     /**
11934      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11935      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11936      */
11937     refresh : function(callback){
11938         if(this.defaultUrl == null){
11939             return;
11940         }
11941         this.update(this.defaultUrl, null, callback, true);
11942     },
11943
11944     /**
11945      * Set this element to auto refresh.
11946      * @param {Number} interval How often to update (in seconds).
11947      * @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)
11948      * @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}
11949      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11950      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11951      */
11952     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11953         if(refreshNow){
11954             this.update(url || this.defaultUrl, params, callback, true);
11955         }
11956         if(this.autoRefreshProcId){
11957             clearInterval(this.autoRefreshProcId);
11958         }
11959         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11960     },
11961
11962     /**
11963      * Stop auto refresh on this element.
11964      */
11965      stopAutoRefresh : function(){
11966         if(this.autoRefreshProcId){
11967             clearInterval(this.autoRefreshProcId);
11968             delete this.autoRefreshProcId;
11969         }
11970     },
11971
11972     isAutoRefreshing : function(){
11973        return this.autoRefreshProcId ? true : false;
11974     },
11975     /**
11976      * Called to update the element to "Loading" state. Override to perform custom action.
11977      */
11978     showLoading : function(){
11979         if(this.showLoadIndicator){
11980             this.el.update(this.indicatorText);
11981         }
11982     },
11983
11984     /**
11985      * Adds unique parameter to query string if disableCaching = true
11986      * @private
11987      */
11988     prepareUrl : function(url){
11989         if(this.disableCaching){
11990             var append = "_dc=" + (new Date().getTime());
11991             if(url.indexOf("?") !== -1){
11992                 url += "&" + append;
11993             }else{
11994                 url += "?" + append;
11995             }
11996         }
11997         return url;
11998     },
11999
12000     /**
12001      * @private
12002      */
12003     processSuccess : function(response){
12004         this.transaction = null;
12005         if(response.argument.form && response.argument.reset){
12006             try{ // put in try/catch since some older FF releases had problems with this
12007                 response.argument.form.reset();
12008             }catch(e){}
12009         }
12010         if(this.loadScripts){
12011             this.renderer.render(this.el, response, this,
12012                 this.updateComplete.createDelegate(this, [response]));
12013         }else{
12014             this.renderer.render(this.el, response, this);
12015             this.updateComplete(response);
12016         }
12017     },
12018
12019     updateComplete : function(response){
12020         this.fireEvent("update", this.el, response);
12021         if(typeof response.argument.callback == "function"){
12022             response.argument.callback(this.el, true, response);
12023         }
12024     },
12025
12026     /**
12027      * @private
12028      */
12029     processFailure : function(response){
12030         this.transaction = null;
12031         this.fireEvent("failure", this.el, response);
12032         if(typeof response.argument.callback == "function"){
12033             response.argument.callback(this.el, false, response);
12034         }
12035     },
12036
12037     /**
12038      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12039      * @param {Object} renderer The object implementing the render() method
12040      */
12041     setRenderer : function(renderer){
12042         this.renderer = renderer;
12043     },
12044
12045     getRenderer : function(){
12046        return this.renderer;
12047     },
12048
12049     /**
12050      * Set the defaultUrl used for updates
12051      * @param {String/Function} defaultUrl The url or a function to call to get the url
12052      */
12053     setDefaultUrl : function(defaultUrl){
12054         this.defaultUrl = defaultUrl;
12055     },
12056
12057     /**
12058      * Aborts the executing transaction
12059      */
12060     abort : function(){
12061         if(this.transaction){
12062             Roo.Ajax.abort(this.transaction);
12063         }
12064     },
12065
12066     /**
12067      * Returns true if an update is in progress
12068      * @return {Boolean}
12069      */
12070     isUpdating : function(){
12071         if(this.transaction){
12072             return Roo.Ajax.isLoading(this.transaction);
12073         }
12074         return false;
12075     }
12076 });
12077
12078 /**
12079  * @class Roo.UpdateManager.defaults
12080  * @static (not really - but it helps the doc tool)
12081  * The defaults collection enables customizing the default properties of UpdateManager
12082  */
12083    Roo.UpdateManager.defaults = {
12084        /**
12085          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12086          * @type Number
12087          */
12088          timeout : 30,
12089
12090          /**
12091          * True to process scripts by default (Defaults to false).
12092          * @type Boolean
12093          */
12094         loadScripts : false,
12095
12096         /**
12097         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12098         * @type String
12099         */
12100         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12101         /**
12102          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12103          * @type Boolean
12104          */
12105         disableCaching : false,
12106         /**
12107          * Whether to show indicatorText when loading (Defaults to true).
12108          * @type Boolean
12109          */
12110         showLoadIndicator : true,
12111         /**
12112          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12113          * @type String
12114          */
12115         indicatorText : '<div class="loading-indicator">Loading...</div>'
12116    };
12117
12118 /**
12119  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12120  *Usage:
12121  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12122  * @param {String/HTMLElement/Roo.Element} el The element to update
12123  * @param {String} url The url
12124  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12125  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12126  * @static
12127  * @deprecated
12128  * @member Roo.UpdateManager
12129  */
12130 Roo.UpdateManager.updateElement = function(el, url, params, options){
12131     var um = Roo.get(el, true).getUpdateManager();
12132     Roo.apply(um, options);
12133     um.update(url, params, options ? options.callback : null);
12134 };
12135 // alias for backwards compat
12136 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12137 /**
12138  * @class Roo.UpdateManager.BasicRenderer
12139  * Default Content renderer. Updates the elements innerHTML with the responseText.
12140  */
12141 Roo.UpdateManager.BasicRenderer = function(){};
12142
12143 Roo.UpdateManager.BasicRenderer.prototype = {
12144     /**
12145      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12146      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12147      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12148      * @param {Roo.Element} el The element being rendered
12149      * @param {Object} response The YUI Connect response object
12150      * @param {UpdateManager} updateManager The calling update manager
12151      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12152      */
12153      render : function(el, response, updateManager, callback){
12154         el.update(response.responseText, updateManager.loadScripts, callback);
12155     }
12156 };
12157 /*
12158  * Based on:
12159  * Roo JS
12160  * (c)) Alan Knowles
12161  * Licence : LGPL
12162  */
12163
12164
12165 /**
12166  * @class Roo.DomTemplate
12167  * @extends Roo.Template
12168  * An effort at a dom based template engine..
12169  *
12170  * Similar to XTemplate, except it uses dom parsing to create the template..
12171  *
12172  * Supported features:
12173  *
12174  *  Tags:
12175
12176 <pre><code>
12177       {a_variable} - output encoded.
12178       {a_variable.format:("Y-m-d")} - call a method on the variable
12179       {a_variable:raw} - unencoded output
12180       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12181       {a_variable:this.method_on_template(...)} - call a method on the template object.
12182  
12183 </code></pre>
12184  *  The tpl tag:
12185 <pre><code>
12186         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12187         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12188         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12189         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12190   
12191 </code></pre>
12192  *      
12193  */
12194 Roo.DomTemplate = function()
12195 {
12196      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12197      if (this.html) {
12198         this.compile();
12199      }
12200 };
12201
12202
12203 Roo.extend(Roo.DomTemplate, Roo.Template, {
12204     /**
12205      * id counter for sub templates.
12206      */
12207     id : 0,
12208     /**
12209      * flag to indicate if dom parser is inside a pre,
12210      * it will strip whitespace if not.
12211      */
12212     inPre : false,
12213     
12214     /**
12215      * The various sub templates
12216      */
12217     tpls : false,
12218     
12219     
12220     
12221     /**
12222      *
12223      * basic tag replacing syntax
12224      * WORD:WORD()
12225      *
12226      * // you can fake an object call by doing this
12227      *  x.t:(test,tesT) 
12228      * 
12229      */
12230     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12231     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12232     
12233     iterChild : function (node, method) {
12234         
12235         var oldPre = this.inPre;
12236         if (node.tagName == 'PRE') {
12237             this.inPre = true;
12238         }
12239         for( var i = 0; i < node.childNodes.length; i++) {
12240             method.call(this, node.childNodes[i]);
12241         }
12242         this.inPre = oldPre;
12243     },
12244     
12245     
12246     
12247     /**
12248      * compile the template
12249      *
12250      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12251      *
12252      */
12253     compile: function()
12254     {
12255         var s = this.html;
12256         
12257         // covert the html into DOM...
12258         var doc = false;
12259         var div =false;
12260         try {
12261             doc = document.implementation.createHTMLDocument("");
12262             doc.documentElement.innerHTML =   this.html  ;
12263             div = doc.documentElement;
12264         } catch (e) {
12265             // old IE... - nasty -- it causes all sorts of issues.. with
12266             // images getting pulled from server..
12267             div = document.createElement('div');
12268             div.innerHTML = this.html;
12269         }
12270         //doc.documentElement.innerHTML = htmlBody
12271          
12272         
12273         
12274         this.tpls = [];
12275         var _t = this;
12276         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12277         
12278         var tpls = this.tpls;
12279         
12280         // create a top level template from the snippet..
12281         
12282         //Roo.log(div.innerHTML);
12283         
12284         var tpl = {
12285             uid : 'master',
12286             id : this.id++,
12287             attr : false,
12288             value : false,
12289             body : div.innerHTML,
12290             
12291             forCall : false,
12292             execCall : false,
12293             dom : div,
12294             isTop : true
12295             
12296         };
12297         tpls.unshift(tpl);
12298         
12299         
12300         // compile them...
12301         this.tpls = [];
12302         Roo.each(tpls, function(tp){
12303             this.compileTpl(tp);
12304             this.tpls[tp.id] = tp;
12305         }, this);
12306         
12307         this.master = tpls[0];
12308         return this;
12309         
12310         
12311     },
12312     
12313     compileNode : function(node, istop) {
12314         // test for
12315         //Roo.log(node);
12316         
12317         
12318         // skip anything not a tag..
12319         if (node.nodeType != 1) {
12320             if (node.nodeType == 3 && !this.inPre) {
12321                 // reduce white space..
12322                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12323                 
12324             }
12325             return;
12326         }
12327         
12328         var tpl = {
12329             uid : false,
12330             id : false,
12331             attr : false,
12332             value : false,
12333             body : '',
12334             
12335             forCall : false,
12336             execCall : false,
12337             dom : false,
12338             isTop : istop
12339             
12340             
12341         };
12342         
12343         
12344         switch(true) {
12345             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12346             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12347             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12348             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12349             // no default..
12350         }
12351         
12352         
12353         if (!tpl.attr) {
12354             // just itterate children..
12355             this.iterChild(node,this.compileNode);
12356             return;
12357         }
12358         tpl.uid = this.id++;
12359         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12360         node.removeAttribute('roo-'+ tpl.attr);
12361         if (tpl.attr != 'name') {
12362             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12363             node.parentNode.replaceChild(placeholder,  node);
12364         } else {
12365             
12366             var placeholder =  document.createElement('span');
12367             placeholder.className = 'roo-tpl-' + tpl.value;
12368             node.parentNode.replaceChild(placeholder,  node);
12369         }
12370         
12371         // parent now sees '{domtplXXXX}
12372         this.iterChild(node,this.compileNode);
12373         
12374         // we should now have node body...
12375         var div = document.createElement('div');
12376         div.appendChild(node);
12377         tpl.dom = node;
12378         // this has the unfortunate side effect of converting tagged attributes
12379         // eg. href="{...}" into %7C...%7D
12380         // this has been fixed by searching for those combo's although it's a bit hacky..
12381         
12382         
12383         tpl.body = div.innerHTML;
12384         
12385         
12386          
12387         tpl.id = tpl.uid;
12388         switch(tpl.attr) {
12389             case 'for' :
12390                 switch (tpl.value) {
12391                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12392                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12393                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12394                 }
12395                 break;
12396             
12397             case 'exec':
12398                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12399                 break;
12400             
12401             case 'if':     
12402                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12403                 break;
12404             
12405             case 'name':
12406                 tpl.id  = tpl.value; // replace non characters???
12407                 break;
12408             
12409         }
12410         
12411         
12412         this.tpls.push(tpl);
12413         
12414         
12415         
12416     },
12417     
12418     
12419     
12420     
12421     /**
12422      * Compile a segment of the template into a 'sub-template'
12423      *
12424      * 
12425      * 
12426      *
12427      */
12428     compileTpl : function(tpl)
12429     {
12430         var fm = Roo.util.Format;
12431         var useF = this.disableFormats !== true;
12432         
12433         var sep = Roo.isGecko ? "+\n" : ",\n";
12434         
12435         var undef = function(str) {
12436             Roo.debug && Roo.log("Property not found :"  + str);
12437             return '';
12438         };
12439           
12440         //Roo.log(tpl.body);
12441         
12442         
12443         
12444         var fn = function(m, lbrace, name, format, args)
12445         {
12446             //Roo.log("ARGS");
12447             //Roo.log(arguments);
12448             args = args ? args.replace(/\\'/g,"'") : args;
12449             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12450             if (typeof(format) == 'undefined') {
12451                 format =  'htmlEncode'; 
12452             }
12453             if (format == 'raw' ) {
12454                 format = false;
12455             }
12456             
12457             if(name.substr(0, 6) == 'domtpl'){
12458                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12459             }
12460             
12461             // build an array of options to determine if value is undefined..
12462             
12463             // basically get 'xxxx.yyyy' then do
12464             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12465             //    (function () { Roo.log("Property not found"); return ''; })() :
12466             //    ......
12467             
12468             var udef_ar = [];
12469             var lookfor = '';
12470             Roo.each(name.split('.'), function(st) {
12471                 lookfor += (lookfor.length ? '.': '') + st;
12472                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12473             });
12474             
12475             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12476             
12477             
12478             if(format && useF){
12479                 
12480                 args = args ? ',' + args : "";
12481                  
12482                 if(format.substr(0, 5) != "this."){
12483                     format = "fm." + format + '(';
12484                 }else{
12485                     format = 'this.call("'+ format.substr(5) + '", ';
12486                     args = ", values";
12487                 }
12488                 
12489                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12490             }
12491              
12492             if (args && args.length) {
12493                 // called with xxyx.yuu:(test,test)
12494                 // change to ()
12495                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12496             }
12497             // raw.. - :raw modifier..
12498             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12499             
12500         };
12501         var body;
12502         // branched to use + in gecko and [].join() in others
12503         if(Roo.isGecko){
12504             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12505                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12506                     "';};};";
12507         }else{
12508             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12509             body.push(tpl.body.replace(/(\r\n|\n)/g,
12510                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12511             body.push("'].join('');};};");
12512             body = body.join('');
12513         }
12514         
12515         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12516        
12517         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12518         eval(body);
12519         
12520         return this;
12521     },
12522      
12523     /**
12524      * same as applyTemplate, except it's done to one of the subTemplates
12525      * when using named templates, you can do:
12526      *
12527      * var str = pl.applySubTemplate('your-name', values);
12528      *
12529      * 
12530      * @param {Number} id of the template
12531      * @param {Object} values to apply to template
12532      * @param {Object} parent (normaly the instance of this object)
12533      */
12534     applySubTemplate : function(id, values, parent)
12535     {
12536         
12537         
12538         var t = this.tpls[id];
12539         
12540         
12541         try { 
12542             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12543                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12544                 return '';
12545             }
12546         } catch(e) {
12547             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12548             Roo.log(values);
12549           
12550             return '';
12551         }
12552         try { 
12553             
12554             if(t.execCall && t.execCall.call(this, values, parent)){
12555                 return '';
12556             }
12557         } catch(e) {
12558             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12559             Roo.log(values);
12560             return '';
12561         }
12562         
12563         try {
12564             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12565             parent = t.target ? values : parent;
12566             if(t.forCall && vs instanceof Array){
12567                 var buf = [];
12568                 for(var i = 0, len = vs.length; i < len; i++){
12569                     try {
12570                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12571                     } catch (e) {
12572                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12573                         Roo.log(e.body);
12574                         //Roo.log(t.compiled);
12575                         Roo.log(vs[i]);
12576                     }   
12577                 }
12578                 return buf.join('');
12579             }
12580         } catch (e) {
12581             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12582             Roo.log(values);
12583             return '';
12584         }
12585         try {
12586             return t.compiled.call(this, vs, parent);
12587         } catch (e) {
12588             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12589             Roo.log(e.body);
12590             //Roo.log(t.compiled);
12591             Roo.log(values);
12592             return '';
12593         }
12594     },
12595
12596    
12597
12598     applyTemplate : function(values){
12599         return this.master.compiled.call(this, values, {});
12600         //var s = this.subs;
12601     },
12602
12603     apply : function(){
12604         return this.applyTemplate.apply(this, arguments);
12605     }
12606
12607  });
12608
12609 Roo.DomTemplate.from = function(el){
12610     el = Roo.getDom(el);
12611     return new Roo.Domtemplate(el.value || el.innerHTML);
12612 };/*
12613  * Based on:
12614  * Ext JS Library 1.1.1
12615  * Copyright(c) 2006-2007, Ext JS, LLC.
12616  *
12617  * Originally Released Under LGPL - original licence link has changed is not relivant.
12618  *
12619  * Fork - LGPL
12620  * <script type="text/javascript">
12621  */
12622
12623 /**
12624  * @class Roo.util.DelayedTask
12625  * Provides a convenient method of performing setTimeout where a new
12626  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12627  * You can use this class to buffer
12628  * the keypress events for a certain number of milliseconds, and perform only if they stop
12629  * for that amount of time.
12630  * @constructor The parameters to this constructor serve as defaults and are not required.
12631  * @param {Function} fn (optional) The default function to timeout
12632  * @param {Object} scope (optional) The default scope of that timeout
12633  * @param {Array} args (optional) The default Array of arguments
12634  */
12635 Roo.util.DelayedTask = function(fn, scope, args){
12636     var id = null, d, t;
12637
12638     var call = function(){
12639         var now = new Date().getTime();
12640         if(now - t >= d){
12641             clearInterval(id);
12642             id = null;
12643             fn.apply(scope, args || []);
12644         }
12645     };
12646     /**
12647      * Cancels any pending timeout and queues a new one
12648      * @param {Number} delay The milliseconds to delay
12649      * @param {Function} newFn (optional) Overrides function passed to constructor
12650      * @param {Object} newScope (optional) Overrides scope passed to constructor
12651      * @param {Array} newArgs (optional) Overrides args passed to constructor
12652      */
12653     this.delay = function(delay, newFn, newScope, newArgs){
12654         if(id && delay != d){
12655             this.cancel();
12656         }
12657         d = delay;
12658         t = new Date().getTime();
12659         fn = newFn || fn;
12660         scope = newScope || scope;
12661         args = newArgs || args;
12662         if(!id){
12663             id = setInterval(call, d);
12664         }
12665     };
12666
12667     /**
12668      * Cancel the last queued timeout
12669      */
12670     this.cancel = function(){
12671         if(id){
12672             clearInterval(id);
12673             id = null;
12674         }
12675     };
12676 };/*
12677  * Based on:
12678  * Ext JS Library 1.1.1
12679  * Copyright(c) 2006-2007, Ext JS, LLC.
12680  *
12681  * Originally Released Under LGPL - original licence link has changed is not relivant.
12682  *
12683  * Fork - LGPL
12684  * <script type="text/javascript">
12685  */
12686  
12687  
12688 Roo.util.TaskRunner = function(interval){
12689     interval = interval || 10;
12690     var tasks = [], removeQueue = [];
12691     var id = 0;
12692     var running = false;
12693
12694     var stopThread = function(){
12695         running = false;
12696         clearInterval(id);
12697         id = 0;
12698     };
12699
12700     var startThread = function(){
12701         if(!running){
12702             running = true;
12703             id = setInterval(runTasks, interval);
12704         }
12705     };
12706
12707     var removeTask = function(task){
12708         removeQueue.push(task);
12709         if(task.onStop){
12710             task.onStop();
12711         }
12712     };
12713
12714     var runTasks = function(){
12715         if(removeQueue.length > 0){
12716             for(var i = 0, len = removeQueue.length; i < len; i++){
12717                 tasks.remove(removeQueue[i]);
12718             }
12719             removeQueue = [];
12720             if(tasks.length < 1){
12721                 stopThread();
12722                 return;
12723             }
12724         }
12725         var now = new Date().getTime();
12726         for(var i = 0, len = tasks.length; i < len; ++i){
12727             var t = tasks[i];
12728             var itime = now - t.taskRunTime;
12729             if(t.interval <= itime){
12730                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12731                 t.taskRunTime = now;
12732                 if(rt === false || t.taskRunCount === t.repeat){
12733                     removeTask(t);
12734                     return;
12735                 }
12736             }
12737             if(t.duration && t.duration <= (now - t.taskStartTime)){
12738                 removeTask(t);
12739             }
12740         }
12741     };
12742
12743     /**
12744      * Queues a new task.
12745      * @param {Object} task
12746      */
12747     this.start = function(task){
12748         tasks.push(task);
12749         task.taskStartTime = new Date().getTime();
12750         task.taskRunTime = 0;
12751         task.taskRunCount = 0;
12752         startThread();
12753         return task;
12754     };
12755
12756     this.stop = function(task){
12757         removeTask(task);
12758         return task;
12759     };
12760
12761     this.stopAll = function(){
12762         stopThread();
12763         for(var i = 0, len = tasks.length; i < len; i++){
12764             if(tasks[i].onStop){
12765                 tasks[i].onStop();
12766             }
12767         }
12768         tasks = [];
12769         removeQueue = [];
12770     };
12771 };
12772
12773 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12774  * Based on:
12775  * Ext JS Library 1.1.1
12776  * Copyright(c) 2006-2007, Ext JS, LLC.
12777  *
12778  * Originally Released Under LGPL - original licence link has changed is not relivant.
12779  *
12780  * Fork - LGPL
12781  * <script type="text/javascript">
12782  */
12783
12784  
12785 /**
12786  * @class Roo.util.MixedCollection
12787  * @extends Roo.util.Observable
12788  * A Collection class that maintains both numeric indexes and keys and exposes events.
12789  * @constructor
12790  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12791  * collection (defaults to false)
12792  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12793  * and return the key value for that item.  This is used when available to look up the key on items that
12794  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12795  * equivalent to providing an implementation for the {@link #getKey} method.
12796  */
12797 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12798     this.items = [];
12799     this.map = {};
12800     this.keys = [];
12801     this.length = 0;
12802     this.addEvents({
12803         /**
12804          * @event clear
12805          * Fires when the collection is cleared.
12806          */
12807         "clear" : true,
12808         /**
12809          * @event add
12810          * Fires when an item is added to the collection.
12811          * @param {Number} index The index at which the item was added.
12812          * @param {Object} o The item added.
12813          * @param {String} key The key associated with the added item.
12814          */
12815         "add" : true,
12816         /**
12817          * @event replace
12818          * Fires when an item is replaced in the collection.
12819          * @param {String} key he key associated with the new added.
12820          * @param {Object} old The item being replaced.
12821          * @param {Object} new The new item.
12822          */
12823         "replace" : true,
12824         /**
12825          * @event remove
12826          * Fires when an item is removed from the collection.
12827          * @param {Object} o The item being removed.
12828          * @param {String} key (optional) The key associated with the removed item.
12829          */
12830         "remove" : true,
12831         "sort" : true
12832     });
12833     this.allowFunctions = allowFunctions === true;
12834     if(keyFn){
12835         this.getKey = keyFn;
12836     }
12837     Roo.util.MixedCollection.superclass.constructor.call(this);
12838 };
12839
12840 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12841     allowFunctions : false,
12842     
12843 /**
12844  * Adds an item to the collection.
12845  * @param {String} key The key to associate with the item
12846  * @param {Object} o The item to add.
12847  * @return {Object} The item added.
12848  */
12849     add : function(key, o){
12850         if(arguments.length == 1){
12851             o = arguments[0];
12852             key = this.getKey(o);
12853         }
12854         if(typeof key == "undefined" || key === null){
12855             this.length++;
12856             this.items.push(o);
12857             this.keys.push(null);
12858         }else{
12859             var old = this.map[key];
12860             if(old){
12861                 return this.replace(key, o);
12862             }
12863             this.length++;
12864             this.items.push(o);
12865             this.map[key] = o;
12866             this.keys.push(key);
12867         }
12868         this.fireEvent("add", this.length-1, o, key);
12869         return o;
12870     },
12871        
12872 /**
12873   * MixedCollection has a generic way to fetch keys if you implement getKey.
12874 <pre><code>
12875 // normal way
12876 var mc = new Roo.util.MixedCollection();
12877 mc.add(someEl.dom.id, someEl);
12878 mc.add(otherEl.dom.id, otherEl);
12879 //and so on
12880
12881 // using getKey
12882 var mc = new Roo.util.MixedCollection();
12883 mc.getKey = function(el){
12884    return el.dom.id;
12885 };
12886 mc.add(someEl);
12887 mc.add(otherEl);
12888
12889 // or via the constructor
12890 var mc = new Roo.util.MixedCollection(false, function(el){
12891    return el.dom.id;
12892 });
12893 mc.add(someEl);
12894 mc.add(otherEl);
12895 </code></pre>
12896  * @param o {Object} The item for which to find the key.
12897  * @return {Object} The key for the passed item.
12898  */
12899     getKey : function(o){
12900          return o.id; 
12901     },
12902    
12903 /**
12904  * Replaces an item in the collection.
12905  * @param {String} key The key associated with the item to replace, or the item to replace.
12906  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12907  * @return {Object}  The new item.
12908  */
12909     replace : function(key, o){
12910         if(arguments.length == 1){
12911             o = arguments[0];
12912             key = this.getKey(o);
12913         }
12914         var old = this.item(key);
12915         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12916              return this.add(key, o);
12917         }
12918         var index = this.indexOfKey(key);
12919         this.items[index] = o;
12920         this.map[key] = o;
12921         this.fireEvent("replace", key, old, o);
12922         return o;
12923     },
12924    
12925 /**
12926  * Adds all elements of an Array or an Object to the collection.
12927  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12928  * an Array of values, each of which are added to the collection.
12929  */
12930     addAll : function(objs){
12931         if(arguments.length > 1 || objs instanceof Array){
12932             var args = arguments.length > 1 ? arguments : objs;
12933             for(var i = 0, len = args.length; i < len; i++){
12934                 this.add(args[i]);
12935             }
12936         }else{
12937             for(var key in objs){
12938                 if(this.allowFunctions || typeof objs[key] != "function"){
12939                     this.add(key, objs[key]);
12940                 }
12941             }
12942         }
12943     },
12944    
12945 /**
12946  * Executes the specified function once for every item in the collection, passing each
12947  * item as the first and only parameter. returning false from the function will stop the iteration.
12948  * @param {Function} fn The function to execute for each item.
12949  * @param {Object} scope (optional) The scope in which to execute the function.
12950  */
12951     each : function(fn, scope){
12952         var items = [].concat(this.items); // each safe for removal
12953         for(var i = 0, len = items.length; i < len; i++){
12954             if(fn.call(scope || items[i], items[i], i, len) === false){
12955                 break;
12956             }
12957         }
12958     },
12959    
12960 /**
12961  * Executes the specified function once for every key in the collection, passing each
12962  * key, and its associated item as the first two parameters.
12963  * @param {Function} fn The function to execute for each item.
12964  * @param {Object} scope (optional) The scope in which to execute the function.
12965  */
12966     eachKey : function(fn, scope){
12967         for(var i = 0, len = this.keys.length; i < len; i++){
12968             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12969         }
12970     },
12971    
12972 /**
12973  * Returns the first item in the collection which elicits a true return value from the
12974  * passed selection function.
12975  * @param {Function} fn The selection function to execute for each item.
12976  * @param {Object} scope (optional) The scope in which to execute the function.
12977  * @return {Object} The first item in the collection which returned true from the selection function.
12978  */
12979     find : function(fn, scope){
12980         for(var i = 0, len = this.items.length; i < len; i++){
12981             if(fn.call(scope || window, this.items[i], this.keys[i])){
12982                 return this.items[i];
12983             }
12984         }
12985         return null;
12986     },
12987    
12988 /**
12989  * Inserts an item at the specified index in the collection.
12990  * @param {Number} index The index to insert the item at.
12991  * @param {String} key The key to associate with the new item, or the item itself.
12992  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12993  * @return {Object} The item inserted.
12994  */
12995     insert : function(index, key, o){
12996         if(arguments.length == 2){
12997             o = arguments[1];
12998             key = this.getKey(o);
12999         }
13000         if(index >= this.length){
13001             return this.add(key, o);
13002         }
13003         this.length++;
13004         this.items.splice(index, 0, o);
13005         if(typeof key != "undefined" && key != null){
13006             this.map[key] = o;
13007         }
13008         this.keys.splice(index, 0, key);
13009         this.fireEvent("add", index, o, key);
13010         return o;
13011     },
13012    
13013 /**
13014  * Removed an item from the collection.
13015  * @param {Object} o The item to remove.
13016  * @return {Object} The item removed.
13017  */
13018     remove : function(o){
13019         return this.removeAt(this.indexOf(o));
13020     },
13021    
13022 /**
13023  * Remove an item from a specified index in the collection.
13024  * @param {Number} index The index within the collection of the item to remove.
13025  */
13026     removeAt : function(index){
13027         if(index < this.length && index >= 0){
13028             this.length--;
13029             var o = this.items[index];
13030             this.items.splice(index, 1);
13031             var key = this.keys[index];
13032             if(typeof key != "undefined"){
13033                 delete this.map[key];
13034             }
13035             this.keys.splice(index, 1);
13036             this.fireEvent("remove", o, key);
13037         }
13038     },
13039    
13040 /**
13041  * Removed an item associated with the passed key fom the collection.
13042  * @param {String} key The key of the item to remove.
13043  */
13044     removeKey : function(key){
13045         return this.removeAt(this.indexOfKey(key));
13046     },
13047    
13048 /**
13049  * Returns the number of items in the collection.
13050  * @return {Number} the number of items in the collection.
13051  */
13052     getCount : function(){
13053         return this.length; 
13054     },
13055    
13056 /**
13057  * Returns index within the collection of the passed Object.
13058  * @param {Object} o The item to find the index of.
13059  * @return {Number} index of the item.
13060  */
13061     indexOf : function(o){
13062         if(!this.items.indexOf){
13063             for(var i = 0, len = this.items.length; i < len; i++){
13064                 if(this.items[i] == o) return i;
13065             }
13066             return -1;
13067         }else{
13068             return this.items.indexOf(o);
13069         }
13070     },
13071    
13072 /**
13073  * Returns index within the collection of the passed key.
13074  * @param {String} key The key to find the index of.
13075  * @return {Number} index of the key.
13076  */
13077     indexOfKey : function(key){
13078         if(!this.keys.indexOf){
13079             for(var i = 0, len = this.keys.length; i < len; i++){
13080                 if(this.keys[i] == key) return i;
13081             }
13082             return -1;
13083         }else{
13084             return this.keys.indexOf(key);
13085         }
13086     },
13087    
13088 /**
13089  * Returns the item associated with the passed key OR index. Key has priority over index.
13090  * @param {String/Number} key The key or index of the item.
13091  * @return {Object} The item associated with the passed key.
13092  */
13093     item : function(key){
13094         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13095         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13096     },
13097     
13098 /**
13099  * Returns the item at the specified index.
13100  * @param {Number} index The index of the item.
13101  * @return {Object}
13102  */
13103     itemAt : function(index){
13104         return this.items[index];
13105     },
13106     
13107 /**
13108  * Returns the item associated with the passed key.
13109  * @param {String/Number} key The key of the item.
13110  * @return {Object} The item associated with the passed key.
13111  */
13112     key : function(key){
13113         return this.map[key];
13114     },
13115    
13116 /**
13117  * Returns true if the collection contains the passed Object as an item.
13118  * @param {Object} o  The Object to look for in the collection.
13119  * @return {Boolean} True if the collection contains the Object as an item.
13120  */
13121     contains : function(o){
13122         return this.indexOf(o) != -1;
13123     },
13124    
13125 /**
13126  * Returns true if the collection contains the passed Object as a key.
13127  * @param {String} key The key to look for in the collection.
13128  * @return {Boolean} True if the collection contains the Object as a key.
13129  */
13130     containsKey : function(key){
13131         return typeof this.map[key] != "undefined";
13132     },
13133    
13134 /**
13135  * Removes all items from the collection.
13136  */
13137     clear : function(){
13138         this.length = 0;
13139         this.items = [];
13140         this.keys = [];
13141         this.map = {};
13142         this.fireEvent("clear");
13143     },
13144    
13145 /**
13146  * Returns the first item in the collection.
13147  * @return {Object} the first item in the collection..
13148  */
13149     first : function(){
13150         return this.items[0]; 
13151     },
13152    
13153 /**
13154  * Returns the last item in the collection.
13155  * @return {Object} the last item in the collection..
13156  */
13157     last : function(){
13158         return this.items[this.length-1];   
13159     },
13160     
13161     _sort : function(property, dir, fn){
13162         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13163         fn = fn || function(a, b){
13164             return a-b;
13165         };
13166         var c = [], k = this.keys, items = this.items;
13167         for(var i = 0, len = items.length; i < len; i++){
13168             c[c.length] = {key: k[i], value: items[i], index: i};
13169         }
13170         c.sort(function(a, b){
13171             var v = fn(a[property], b[property]) * dsc;
13172             if(v == 0){
13173                 v = (a.index < b.index ? -1 : 1);
13174             }
13175             return v;
13176         });
13177         for(var i = 0, len = c.length; i < len; i++){
13178             items[i] = c[i].value;
13179             k[i] = c[i].key;
13180         }
13181         this.fireEvent("sort", this);
13182     },
13183     
13184     /**
13185      * Sorts this collection with the passed comparison function
13186      * @param {String} direction (optional) "ASC" or "DESC"
13187      * @param {Function} fn (optional) comparison function
13188      */
13189     sort : function(dir, fn){
13190         this._sort("value", dir, fn);
13191     },
13192     
13193     /**
13194      * Sorts this collection by keys
13195      * @param {String} direction (optional) "ASC" or "DESC"
13196      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13197      */
13198     keySort : function(dir, fn){
13199         this._sort("key", dir, fn || function(a, b){
13200             return String(a).toUpperCase()-String(b).toUpperCase();
13201         });
13202     },
13203     
13204     /**
13205      * Returns a range of items in this collection
13206      * @param {Number} startIndex (optional) defaults to 0
13207      * @param {Number} endIndex (optional) default to the last item
13208      * @return {Array} An array of items
13209      */
13210     getRange : function(start, end){
13211         var items = this.items;
13212         if(items.length < 1){
13213             return [];
13214         }
13215         start = start || 0;
13216         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13217         var r = [];
13218         if(start <= end){
13219             for(var i = start; i <= end; i++) {
13220                     r[r.length] = items[i];
13221             }
13222         }else{
13223             for(var i = start; i >= end; i--) {
13224                     r[r.length] = items[i];
13225             }
13226         }
13227         return r;
13228     },
13229         
13230     /**
13231      * Filter the <i>objects</i> in this collection by a specific property. 
13232      * Returns a new collection that has been filtered.
13233      * @param {String} property A property on your objects
13234      * @param {String/RegExp} value Either string that the property values 
13235      * should start with or a RegExp to test against the property
13236      * @return {MixedCollection} The new filtered collection
13237      */
13238     filter : function(property, value){
13239         if(!value.exec){ // not a regex
13240             value = String(value);
13241             if(value.length == 0){
13242                 return this.clone();
13243             }
13244             value = new RegExp("^" + Roo.escapeRe(value), "i");
13245         }
13246         return this.filterBy(function(o){
13247             return o && value.test(o[property]);
13248         });
13249         },
13250     
13251     /**
13252      * Filter by a function. * Returns a new collection that has been filtered.
13253      * The passed function will be called with each 
13254      * object in the collection. If the function returns true, the value is included 
13255      * otherwise it is filtered.
13256      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13257      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13258      * @return {MixedCollection} The new filtered collection
13259      */
13260     filterBy : function(fn, scope){
13261         var r = new Roo.util.MixedCollection();
13262         r.getKey = this.getKey;
13263         var k = this.keys, it = this.items;
13264         for(var i = 0, len = it.length; i < len; i++){
13265             if(fn.call(scope||this, it[i], k[i])){
13266                                 r.add(k[i], it[i]);
13267                         }
13268         }
13269         return r;
13270     },
13271     
13272     /**
13273      * Creates a duplicate of this collection
13274      * @return {MixedCollection}
13275      */
13276     clone : function(){
13277         var r = new Roo.util.MixedCollection();
13278         var k = this.keys, it = this.items;
13279         for(var i = 0, len = it.length; i < len; i++){
13280             r.add(k[i], it[i]);
13281         }
13282         r.getKey = this.getKey;
13283         return r;
13284     }
13285 });
13286 /**
13287  * Returns the item associated with the passed key or index.
13288  * @method
13289  * @param {String/Number} key The key or index of the item.
13290  * @return {Object} The item associated with the passed key.
13291  */
13292 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13293  * Based on:
13294  * Ext JS Library 1.1.1
13295  * Copyright(c) 2006-2007, Ext JS, LLC.
13296  *
13297  * Originally Released Under LGPL - original licence link has changed is not relivant.
13298  *
13299  * Fork - LGPL
13300  * <script type="text/javascript">
13301  */
13302 /**
13303  * @class Roo.util.JSON
13304  * Modified version of Douglas Crockford"s json.js that doesn"t
13305  * mess with the Object prototype 
13306  * http://www.json.org/js.html
13307  * @singleton
13308  */
13309 Roo.util.JSON = new (function(){
13310     var useHasOwn = {}.hasOwnProperty ? true : false;
13311     
13312     // crashes Safari in some instances
13313     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13314     
13315     var pad = function(n) {
13316         return n < 10 ? "0" + n : n;
13317     };
13318     
13319     var m = {
13320         "\b": '\\b',
13321         "\t": '\\t',
13322         "\n": '\\n',
13323         "\f": '\\f',
13324         "\r": '\\r',
13325         '"' : '\\"',
13326         "\\": '\\\\'
13327     };
13328
13329     var encodeString = function(s){
13330         if (/["\\\x00-\x1f]/.test(s)) {
13331             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13332                 var c = m[b];
13333                 if(c){
13334                     return c;
13335                 }
13336                 c = b.charCodeAt();
13337                 return "\\u00" +
13338                     Math.floor(c / 16).toString(16) +
13339                     (c % 16).toString(16);
13340             }) + '"';
13341         }
13342         return '"' + s + '"';
13343     };
13344     
13345     var encodeArray = function(o){
13346         var a = ["["], b, i, l = o.length, v;
13347             for (i = 0; i < l; i += 1) {
13348                 v = o[i];
13349                 switch (typeof v) {
13350                     case "undefined":
13351                     case "function":
13352                     case "unknown":
13353                         break;
13354                     default:
13355                         if (b) {
13356                             a.push(',');
13357                         }
13358                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13359                         b = true;
13360                 }
13361             }
13362             a.push("]");
13363             return a.join("");
13364     };
13365     
13366     var encodeDate = function(o){
13367         return '"' + o.getFullYear() + "-" +
13368                 pad(o.getMonth() + 1) + "-" +
13369                 pad(o.getDate()) + "T" +
13370                 pad(o.getHours()) + ":" +
13371                 pad(o.getMinutes()) + ":" +
13372                 pad(o.getSeconds()) + '"';
13373     };
13374     
13375     /**
13376      * Encodes an Object, Array or other value
13377      * @param {Mixed} o The variable to encode
13378      * @return {String} The JSON string
13379      */
13380     this.encode = function(o)
13381     {
13382         // should this be extended to fully wrap stringify..
13383         
13384         if(typeof o == "undefined" || o === null){
13385             return "null";
13386         }else if(o instanceof Array){
13387             return encodeArray(o);
13388         }else if(o instanceof Date){
13389             return encodeDate(o);
13390         }else if(typeof o == "string"){
13391             return encodeString(o);
13392         }else if(typeof o == "number"){
13393             return isFinite(o) ? String(o) : "null";
13394         }else if(typeof o == "boolean"){
13395             return String(o);
13396         }else {
13397             var a = ["{"], b, i, v;
13398             for (i in o) {
13399                 if(!useHasOwn || o.hasOwnProperty(i)) {
13400                     v = o[i];
13401                     switch (typeof v) {
13402                     case "undefined":
13403                     case "function":
13404                     case "unknown":
13405                         break;
13406                     default:
13407                         if(b){
13408                             a.push(',');
13409                         }
13410                         a.push(this.encode(i), ":",
13411                                 v === null ? "null" : this.encode(v));
13412                         b = true;
13413                     }
13414                 }
13415             }
13416             a.push("}");
13417             return a.join("");
13418         }
13419     };
13420     
13421     /**
13422      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13423      * @param {String} json The JSON string
13424      * @return {Object} The resulting object
13425      */
13426     this.decode = function(json){
13427         
13428         return  /** eval:var:json */ eval("(" + json + ')');
13429     };
13430 })();
13431 /** 
13432  * Shorthand for {@link Roo.util.JSON#encode}
13433  * @member Roo encode 
13434  * @method */
13435 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13436 /** 
13437  * Shorthand for {@link Roo.util.JSON#decode}
13438  * @member Roo decode 
13439  * @method */
13440 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13441 /*
13442  * Based on:
13443  * Ext JS Library 1.1.1
13444  * Copyright(c) 2006-2007, Ext JS, LLC.
13445  *
13446  * Originally Released Under LGPL - original licence link has changed is not relivant.
13447  *
13448  * Fork - LGPL
13449  * <script type="text/javascript">
13450  */
13451  
13452 /**
13453  * @class Roo.util.Format
13454  * Reusable data formatting functions
13455  * @singleton
13456  */
13457 Roo.util.Format = function(){
13458     var trimRe = /^\s+|\s+$/g;
13459     return {
13460         /**
13461          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13462          * @param {String} value The string to truncate
13463          * @param {Number} length The maximum length to allow before truncating
13464          * @return {String} The converted text
13465          */
13466         ellipsis : function(value, len){
13467             if(value && value.length > len){
13468                 return value.substr(0, len-3)+"...";
13469             }
13470             return value;
13471         },
13472
13473         /**
13474          * Checks a reference and converts it to empty string if it is undefined
13475          * @param {Mixed} value Reference to check
13476          * @return {Mixed} Empty string if converted, otherwise the original value
13477          */
13478         undef : function(value){
13479             return typeof value != "undefined" ? value : "";
13480         },
13481
13482         /**
13483          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13484          * @param {String} value The string to encode
13485          * @return {String} The encoded text
13486          */
13487         htmlEncode : function(value){
13488             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13489         },
13490
13491         /**
13492          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13493          * @param {String} value The string to decode
13494          * @return {String} The decoded text
13495          */
13496         htmlDecode : function(value){
13497             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13498         },
13499
13500         /**
13501          * Trims any whitespace from either side of a string
13502          * @param {String} value The text to trim
13503          * @return {String} The trimmed text
13504          */
13505         trim : function(value){
13506             return String(value).replace(trimRe, "");
13507         },
13508
13509         /**
13510          * Returns a substring from within an original string
13511          * @param {String} value The original text
13512          * @param {Number} start The start index of the substring
13513          * @param {Number} length The length of the substring
13514          * @return {String} The substring
13515          */
13516         substr : function(value, start, length){
13517             return String(value).substr(start, length);
13518         },
13519
13520         /**
13521          * Converts a string to all lower case letters
13522          * @param {String} value The text to convert
13523          * @return {String} The converted text
13524          */
13525         lowercase : function(value){
13526             return String(value).toLowerCase();
13527         },
13528
13529         /**
13530          * Converts a string to all upper case letters
13531          * @param {String} value The text to convert
13532          * @return {String} The converted text
13533          */
13534         uppercase : function(value){
13535             return String(value).toUpperCase();
13536         },
13537
13538         /**
13539          * Converts the first character only of a string to upper case
13540          * @param {String} value The text to convert
13541          * @return {String} The converted text
13542          */
13543         capitalize : function(value){
13544             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13545         },
13546
13547         // private
13548         call : function(value, fn){
13549             if(arguments.length > 2){
13550                 var args = Array.prototype.slice.call(arguments, 2);
13551                 args.unshift(value);
13552                  
13553                 return /** eval:var:value */  eval(fn).apply(window, args);
13554             }else{
13555                 /** eval:var:value */
13556                 return /** eval:var:value */ eval(fn).call(window, value);
13557             }
13558         },
13559
13560        
13561         /**
13562          * safer version of Math.toFixed..??/
13563          * @param {Number/String} value The numeric value to format
13564          * @param {Number/String} value Decimal places 
13565          * @return {String} The formatted currency string
13566          */
13567         toFixed : function(v, n)
13568         {
13569             // why not use to fixed - precision is buggered???
13570             if (!n) {
13571                 return Math.round(v-0);
13572             }
13573             var fact = Math.pow(10,n+1);
13574             v = (Math.round((v-0)*fact))/fact;
13575             var z = (''+fact).substring(2);
13576             if (v == Math.floor(v)) {
13577                 return Math.floor(v) + '.' + z;
13578             }
13579             
13580             // now just padd decimals..
13581             var ps = String(v).split('.');
13582             var fd = (ps[1] + z);
13583             var r = fd.substring(0,n); 
13584             var rm = fd.substring(n); 
13585             if (rm < 5) {
13586                 return ps[0] + '.' + r;
13587             }
13588             r*=1; // turn it into a number;
13589             r++;
13590             if (String(r).length != n) {
13591                 ps[0]*=1;
13592                 ps[0]++;
13593                 r = String(r).substring(1); // chop the end off.
13594             }
13595             
13596             return ps[0] + '.' + r;
13597              
13598         },
13599         
13600         /**
13601          * Format a number as US currency
13602          * @param {Number/String} value The numeric value to format
13603          * @return {String} The formatted currency string
13604          */
13605         usMoney : function(v){
13606             return '$' + Roo.util.Format.number(v);
13607         },
13608         
13609         /**
13610          * Format a number
13611          * eventually this should probably emulate php's number_format
13612          * @param {Number/String} value The numeric value to format
13613          * @param {Number} decimals number of decimal places
13614          * @return {String} The formatted currency string
13615          */
13616         number : function(v,decimals)
13617         {
13618             // multiply and round.
13619             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13620             var mul = Math.pow(10, decimals);
13621             var zero = String(mul).substring(1);
13622             v = (Math.round((v-0)*mul))/mul;
13623             
13624             // if it's '0' number.. then
13625             
13626             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13627             v = String(v);
13628             var ps = v.split('.');
13629             var whole = ps[0];
13630             
13631             
13632             var r = /(\d+)(\d{3})/;
13633             // add comma's
13634             while (r.test(whole)) {
13635                 whole = whole.replace(r, '$1' + ',' + '$2');
13636             }
13637             
13638             
13639             var sub = ps[1] ?
13640                     // has decimals..
13641                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13642                     // does not have decimals
13643                     (decimals ? ('.' + zero) : '');
13644             
13645             
13646             return whole + sub ;
13647         },
13648         
13649         /**
13650          * Parse a value into a formatted date using the specified format pattern.
13651          * @param {Mixed} value The value to format
13652          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13653          * @return {String} The formatted date string
13654          */
13655         date : function(v, format){
13656             if(!v){
13657                 return "";
13658             }
13659             if(!(v instanceof Date)){
13660                 v = new Date(Date.parse(v));
13661             }
13662             return v.dateFormat(format || Roo.util.Format.defaults.date);
13663         },
13664
13665         /**
13666          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13667          * @param {String} format Any valid date format string
13668          * @return {Function} The date formatting function
13669          */
13670         dateRenderer : function(format){
13671             return function(v){
13672                 return Roo.util.Format.date(v, format);  
13673             };
13674         },
13675
13676         // private
13677         stripTagsRE : /<\/?[^>]+>/gi,
13678         
13679         /**
13680          * Strips all HTML tags
13681          * @param {Mixed} value The text from which to strip tags
13682          * @return {String} The stripped text
13683          */
13684         stripTags : function(v){
13685             return !v ? v : String(v).replace(this.stripTagsRE, "");
13686         }
13687     };
13688 }();
13689 Roo.util.Format.defaults = {
13690     date : 'd/M/Y'
13691 };/*
13692  * Based on:
13693  * Ext JS Library 1.1.1
13694  * Copyright(c) 2006-2007, Ext JS, LLC.
13695  *
13696  * Originally Released Under LGPL - original licence link has changed is not relivant.
13697  *
13698  * Fork - LGPL
13699  * <script type="text/javascript">
13700  */
13701
13702
13703  
13704
13705 /**
13706  * @class Roo.MasterTemplate
13707  * @extends Roo.Template
13708  * Provides a template that can have child templates. The syntax is:
13709 <pre><code>
13710 var t = new Roo.MasterTemplate(
13711         '&lt;select name="{name}"&gt;',
13712                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13713         '&lt;/select&gt;'
13714 );
13715 t.add('options', {value: 'foo', text: 'bar'});
13716 // or you can add multiple child elements in one shot
13717 t.addAll('options', [
13718     {value: 'foo', text: 'bar'},
13719     {value: 'foo2', text: 'bar2'},
13720     {value: 'foo3', text: 'bar3'}
13721 ]);
13722 // then append, applying the master template values
13723 t.append('my-form', {name: 'my-select'});
13724 </code></pre>
13725 * A name attribute for the child template is not required if you have only one child
13726 * template or you want to refer to them by index.
13727  */
13728 Roo.MasterTemplate = function(){
13729     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13730     this.originalHtml = this.html;
13731     var st = {};
13732     var m, re = this.subTemplateRe;
13733     re.lastIndex = 0;
13734     var subIndex = 0;
13735     while(m = re.exec(this.html)){
13736         var name = m[1], content = m[2];
13737         st[subIndex] = {
13738             name: name,
13739             index: subIndex,
13740             buffer: [],
13741             tpl : new Roo.Template(content)
13742         };
13743         if(name){
13744             st[name] = st[subIndex];
13745         }
13746         st[subIndex].tpl.compile();
13747         st[subIndex].tpl.call = this.call.createDelegate(this);
13748         subIndex++;
13749     }
13750     this.subCount = subIndex;
13751     this.subs = st;
13752 };
13753 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13754     /**
13755     * The regular expression used to match sub templates
13756     * @type RegExp
13757     * @property
13758     */
13759     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13760
13761     /**
13762      * Applies the passed values to a child template.
13763      * @param {String/Number} name (optional) The name or index of the child template
13764      * @param {Array/Object} values The values to be applied to the template
13765      * @return {MasterTemplate} this
13766      */
13767      add : function(name, values){
13768         if(arguments.length == 1){
13769             values = arguments[0];
13770             name = 0;
13771         }
13772         var s = this.subs[name];
13773         s.buffer[s.buffer.length] = s.tpl.apply(values);
13774         return this;
13775     },
13776
13777     /**
13778      * Applies all the passed values to a child template.
13779      * @param {String/Number} name (optional) The name or index of the child template
13780      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13781      * @param {Boolean} reset (optional) True to reset the template first
13782      * @return {MasterTemplate} this
13783      */
13784     fill : function(name, values, reset){
13785         var a = arguments;
13786         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13787             values = a[0];
13788             name = 0;
13789             reset = a[1];
13790         }
13791         if(reset){
13792             this.reset();
13793         }
13794         for(var i = 0, len = values.length; i < len; i++){
13795             this.add(name, values[i]);
13796         }
13797         return this;
13798     },
13799
13800     /**
13801      * Resets the template for reuse
13802      * @return {MasterTemplate} this
13803      */
13804      reset : function(){
13805         var s = this.subs;
13806         for(var i = 0; i < this.subCount; i++){
13807             s[i].buffer = [];
13808         }
13809         return this;
13810     },
13811
13812     applyTemplate : function(values){
13813         var s = this.subs;
13814         var replaceIndex = -1;
13815         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13816             return s[++replaceIndex].buffer.join("");
13817         });
13818         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13819     },
13820
13821     apply : function(){
13822         return this.applyTemplate.apply(this, arguments);
13823     },
13824
13825     compile : function(){return this;}
13826 });
13827
13828 /**
13829  * Alias for fill().
13830  * @method
13831  */
13832 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13833  /**
13834  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13835  * var tpl = Roo.MasterTemplate.from('element-id');
13836  * @param {String/HTMLElement} el
13837  * @param {Object} config
13838  * @static
13839  */
13840 Roo.MasterTemplate.from = function(el, config){
13841     el = Roo.getDom(el);
13842     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13843 };/*
13844  * Based on:
13845  * Ext JS Library 1.1.1
13846  * Copyright(c) 2006-2007, Ext JS, LLC.
13847  *
13848  * Originally Released Under LGPL - original licence link has changed is not relivant.
13849  *
13850  * Fork - LGPL
13851  * <script type="text/javascript">
13852  */
13853
13854  
13855 /**
13856  * @class Roo.util.CSS
13857  * Utility class for manipulating CSS rules
13858  * @singleton
13859  */
13860 Roo.util.CSS = function(){
13861         var rules = null;
13862         var doc = document;
13863
13864     var camelRe = /(-[a-z])/gi;
13865     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13866
13867    return {
13868    /**
13869     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13870     * tag and appended to the HEAD of the document.
13871     * @param {String|Object} cssText The text containing the css rules
13872     * @param {String} id An id to add to the stylesheet for later removal
13873     * @return {StyleSheet}
13874     */
13875     createStyleSheet : function(cssText, id){
13876         var ss;
13877         var head = doc.getElementsByTagName("head")[0];
13878         var nrules = doc.createElement("style");
13879         nrules.setAttribute("type", "text/css");
13880         if(id){
13881             nrules.setAttribute("id", id);
13882         }
13883         if (typeof(cssText) != 'string') {
13884             // support object maps..
13885             // not sure if this a good idea.. 
13886             // perhaps it should be merged with the general css handling
13887             // and handle js style props.
13888             var cssTextNew = [];
13889             for(var n in cssText) {
13890                 var citems = [];
13891                 for(var k in cssText[n]) {
13892                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13893                 }
13894                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13895                 
13896             }
13897             cssText = cssTextNew.join("\n");
13898             
13899         }
13900        
13901        
13902        if(Roo.isIE){
13903            head.appendChild(nrules);
13904            ss = nrules.styleSheet;
13905            ss.cssText = cssText;
13906        }else{
13907            try{
13908                 nrules.appendChild(doc.createTextNode(cssText));
13909            }catch(e){
13910                nrules.cssText = cssText; 
13911            }
13912            head.appendChild(nrules);
13913            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13914        }
13915        this.cacheStyleSheet(ss);
13916        return ss;
13917    },
13918
13919    /**
13920     * Removes a style or link tag by id
13921     * @param {String} id The id of the tag
13922     */
13923    removeStyleSheet : function(id){
13924        var existing = doc.getElementById(id);
13925        if(existing){
13926            existing.parentNode.removeChild(existing);
13927        }
13928    },
13929
13930    /**
13931     * Dynamically swaps an existing stylesheet reference for a new one
13932     * @param {String} id The id of an existing link tag to remove
13933     * @param {String} url The href of the new stylesheet to include
13934     */
13935    swapStyleSheet : function(id, url){
13936        this.removeStyleSheet(id);
13937        var ss = doc.createElement("link");
13938        ss.setAttribute("rel", "stylesheet");
13939        ss.setAttribute("type", "text/css");
13940        ss.setAttribute("id", id);
13941        ss.setAttribute("href", url);
13942        doc.getElementsByTagName("head")[0].appendChild(ss);
13943    },
13944    
13945    /**
13946     * Refresh the rule cache if you have dynamically added stylesheets
13947     * @return {Object} An object (hash) of rules indexed by selector
13948     */
13949    refreshCache : function(){
13950        return this.getRules(true);
13951    },
13952
13953    // private
13954    cacheStyleSheet : function(stylesheet){
13955        if(!rules){
13956            rules = {};
13957        }
13958        try{// try catch for cross domain access issue
13959            var ssRules = stylesheet.cssRules || stylesheet.rules;
13960            for(var j = ssRules.length-1; j >= 0; --j){
13961                rules[ssRules[j].selectorText] = ssRules[j];
13962            }
13963        }catch(e){}
13964    },
13965    
13966    /**
13967     * Gets all css rules for the document
13968     * @param {Boolean} refreshCache true to refresh the internal cache
13969     * @return {Object} An object (hash) of rules indexed by selector
13970     */
13971    getRules : function(refreshCache){
13972                 if(rules == null || refreshCache){
13973                         rules = {};
13974                         var ds = doc.styleSheets;
13975                         for(var i =0, len = ds.length; i < len; i++){
13976                             try{
13977                         this.cacheStyleSheet(ds[i]);
13978                     }catch(e){} 
13979                 }
13980                 }
13981                 return rules;
13982         },
13983         
13984         /**
13985     * Gets an an individual CSS rule by selector(s)
13986     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13987     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13988     * @return {CSSRule} The CSS rule or null if one is not found
13989     */
13990    getRule : function(selector, refreshCache){
13991                 var rs = this.getRules(refreshCache);
13992                 if(!(selector instanceof Array)){
13993                     return rs[selector];
13994                 }
13995                 for(var i = 0; i < selector.length; i++){
13996                         if(rs[selector[i]]){
13997                                 return rs[selector[i]];
13998                         }
13999                 }
14000                 return null;
14001         },
14002         
14003         
14004         /**
14005     * Updates a rule property
14006     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14007     * @param {String} property The css property
14008     * @param {String} value The new value for the property
14009     * @return {Boolean} true If a rule was found and updated
14010     */
14011    updateRule : function(selector, property, value){
14012                 if(!(selector instanceof Array)){
14013                         var rule = this.getRule(selector);
14014                         if(rule){
14015                                 rule.style[property.replace(camelRe, camelFn)] = value;
14016                                 return true;
14017                         }
14018                 }else{
14019                         for(var i = 0; i < selector.length; i++){
14020                                 if(this.updateRule(selector[i], property, value)){
14021                                         return true;
14022                                 }
14023                         }
14024                 }
14025                 return false;
14026         }
14027    };   
14028 }();/*
14029  * Based on:
14030  * Ext JS Library 1.1.1
14031  * Copyright(c) 2006-2007, Ext JS, LLC.
14032  *
14033  * Originally Released Under LGPL - original licence link has changed is not relivant.
14034  *
14035  * Fork - LGPL
14036  * <script type="text/javascript">
14037  */
14038
14039  
14040
14041 /**
14042  * @class Roo.util.ClickRepeater
14043  * @extends Roo.util.Observable
14044  * 
14045  * A wrapper class which can be applied to any element. Fires a "click" event while the
14046  * mouse is pressed. The interval between firings may be specified in the config but
14047  * defaults to 10 milliseconds.
14048  * 
14049  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14050  * 
14051  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14052  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14053  * Similar to an autorepeat key delay.
14054  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14055  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14056  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14057  *           "interval" and "delay" are ignored. "immediate" is honored.
14058  * @cfg {Boolean} preventDefault True to prevent the default click event
14059  * @cfg {Boolean} stopDefault True to stop the default click event
14060  * 
14061  * @history
14062  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14063  *     2007-02-02 jvs Renamed to ClickRepeater
14064  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14065  *
14066  *  @constructor
14067  * @param {String/HTMLElement/Element} el The element to listen on
14068  * @param {Object} config
14069  **/
14070 Roo.util.ClickRepeater = function(el, config)
14071 {
14072     this.el = Roo.get(el);
14073     this.el.unselectable();
14074
14075     Roo.apply(this, config);
14076
14077     this.addEvents({
14078     /**
14079      * @event mousedown
14080      * Fires when the mouse button is depressed.
14081      * @param {Roo.util.ClickRepeater} this
14082      */
14083         "mousedown" : true,
14084     /**
14085      * @event click
14086      * Fires on a specified interval during the time the element is pressed.
14087      * @param {Roo.util.ClickRepeater} this
14088      */
14089         "click" : true,
14090     /**
14091      * @event mouseup
14092      * Fires when the mouse key is released.
14093      * @param {Roo.util.ClickRepeater} this
14094      */
14095         "mouseup" : true
14096     });
14097
14098     this.el.on("mousedown", this.handleMouseDown, this);
14099     if(this.preventDefault || this.stopDefault){
14100         this.el.on("click", function(e){
14101             if(this.preventDefault){
14102                 e.preventDefault();
14103             }
14104             if(this.stopDefault){
14105                 e.stopEvent();
14106             }
14107         }, this);
14108     }
14109
14110     // allow inline handler
14111     if(this.handler){
14112         this.on("click", this.handler,  this.scope || this);
14113     }
14114
14115     Roo.util.ClickRepeater.superclass.constructor.call(this);
14116 };
14117
14118 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14119     interval : 20,
14120     delay: 250,
14121     preventDefault : true,
14122     stopDefault : false,
14123     timer : 0,
14124
14125     // private
14126     handleMouseDown : function(){
14127         clearTimeout(this.timer);
14128         this.el.blur();
14129         if(this.pressClass){
14130             this.el.addClass(this.pressClass);
14131         }
14132         this.mousedownTime = new Date();
14133
14134         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14135         this.el.on("mouseout", this.handleMouseOut, this);
14136
14137         this.fireEvent("mousedown", this);
14138         this.fireEvent("click", this);
14139         
14140         this.timer = this.click.defer(this.delay || this.interval, this);
14141     },
14142
14143     // private
14144     click : function(){
14145         this.fireEvent("click", this);
14146         this.timer = this.click.defer(this.getInterval(), this);
14147     },
14148
14149     // private
14150     getInterval: function(){
14151         if(!this.accelerate){
14152             return this.interval;
14153         }
14154         var pressTime = this.mousedownTime.getElapsed();
14155         if(pressTime < 500){
14156             return 400;
14157         }else if(pressTime < 1700){
14158             return 320;
14159         }else if(pressTime < 2600){
14160             return 250;
14161         }else if(pressTime < 3500){
14162             return 180;
14163         }else if(pressTime < 4400){
14164             return 140;
14165         }else if(pressTime < 5300){
14166             return 80;
14167         }else if(pressTime < 6200){
14168             return 50;
14169         }else{
14170             return 10;
14171         }
14172     },
14173
14174     // private
14175     handleMouseOut : function(){
14176         clearTimeout(this.timer);
14177         if(this.pressClass){
14178             this.el.removeClass(this.pressClass);
14179         }
14180         this.el.on("mouseover", this.handleMouseReturn, this);
14181     },
14182
14183     // private
14184     handleMouseReturn : function(){
14185         this.el.un("mouseover", this.handleMouseReturn);
14186         if(this.pressClass){
14187             this.el.addClass(this.pressClass);
14188         }
14189         this.click();
14190     },
14191
14192     // private
14193     handleMouseUp : function(){
14194         clearTimeout(this.timer);
14195         this.el.un("mouseover", this.handleMouseReturn);
14196         this.el.un("mouseout", this.handleMouseOut);
14197         Roo.get(document).un("mouseup", this.handleMouseUp);
14198         this.el.removeClass(this.pressClass);
14199         this.fireEvent("mouseup", this);
14200     }
14201 });/*
14202  * Based on:
14203  * Ext JS Library 1.1.1
14204  * Copyright(c) 2006-2007, Ext JS, LLC.
14205  *
14206  * Originally Released Under LGPL - original licence link has changed is not relivant.
14207  *
14208  * Fork - LGPL
14209  * <script type="text/javascript">
14210  */
14211
14212  
14213 /**
14214  * @class Roo.KeyNav
14215  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14216  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14217  * way to implement custom navigation schemes for any UI component.</p>
14218  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14219  * pageUp, pageDown, del, home, end.  Usage:</p>
14220  <pre><code>
14221 var nav = new Roo.KeyNav("my-element", {
14222     "left" : function(e){
14223         this.moveLeft(e.ctrlKey);
14224     },
14225     "right" : function(e){
14226         this.moveRight(e.ctrlKey);
14227     },
14228     "enter" : function(e){
14229         this.save();
14230     },
14231     scope : this
14232 });
14233 </code></pre>
14234  * @constructor
14235  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14236  * @param {Object} config The config
14237  */
14238 Roo.KeyNav = function(el, config){
14239     this.el = Roo.get(el);
14240     Roo.apply(this, config);
14241     if(!this.disabled){
14242         this.disabled = true;
14243         this.enable();
14244     }
14245 };
14246
14247 Roo.KeyNav.prototype = {
14248     /**
14249      * @cfg {Boolean} disabled
14250      * True to disable this KeyNav instance (defaults to false)
14251      */
14252     disabled : false,
14253     /**
14254      * @cfg {String} defaultEventAction
14255      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14256      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14257      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14258      */
14259     defaultEventAction: "stopEvent",
14260     /**
14261      * @cfg {Boolean} forceKeyDown
14262      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14263      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14264      * handle keydown instead of keypress.
14265      */
14266     forceKeyDown : false,
14267
14268     // private
14269     prepareEvent : function(e){
14270         var k = e.getKey();
14271         var h = this.keyToHandler[k];
14272         //if(h && this[h]){
14273         //    e.stopPropagation();
14274         //}
14275         if(Roo.isSafari && h && k >= 37 && k <= 40){
14276             e.stopEvent();
14277         }
14278     },
14279
14280     // private
14281     relay : function(e){
14282         var k = e.getKey();
14283         var h = this.keyToHandler[k];
14284         if(h && this[h]){
14285             if(this.doRelay(e, this[h], h) !== true){
14286                 e[this.defaultEventAction]();
14287             }
14288         }
14289     },
14290
14291     // private
14292     doRelay : function(e, h, hname){
14293         return h.call(this.scope || this, e);
14294     },
14295
14296     // possible handlers
14297     enter : false,
14298     left : false,
14299     right : false,
14300     up : false,
14301     down : false,
14302     tab : false,
14303     esc : false,
14304     pageUp : false,
14305     pageDown : false,
14306     del : false,
14307     home : false,
14308     end : false,
14309
14310     // quick lookup hash
14311     keyToHandler : {
14312         37 : "left",
14313         39 : "right",
14314         38 : "up",
14315         40 : "down",
14316         33 : "pageUp",
14317         34 : "pageDown",
14318         46 : "del",
14319         36 : "home",
14320         35 : "end",
14321         13 : "enter",
14322         27 : "esc",
14323         9  : "tab"
14324     },
14325
14326         /**
14327          * Enable this KeyNav
14328          */
14329         enable: function(){
14330                 if(this.disabled){
14331             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14332             // the EventObject will normalize Safari automatically
14333             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14334                 this.el.on("keydown", this.relay,  this);
14335             }else{
14336                 this.el.on("keydown", this.prepareEvent,  this);
14337                 this.el.on("keypress", this.relay,  this);
14338             }
14339                     this.disabled = false;
14340                 }
14341         },
14342
14343         /**
14344          * Disable this KeyNav
14345          */
14346         disable: function(){
14347                 if(!this.disabled){
14348                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14349                 this.el.un("keydown", this.relay);
14350             }else{
14351                 this.el.un("keydown", this.prepareEvent);
14352                 this.el.un("keypress", this.relay);
14353             }
14354                     this.disabled = true;
14355                 }
14356         }
14357 };/*
14358  * Based on:
14359  * Ext JS Library 1.1.1
14360  * Copyright(c) 2006-2007, Ext JS, LLC.
14361  *
14362  * Originally Released Under LGPL - original licence link has changed is not relivant.
14363  *
14364  * Fork - LGPL
14365  * <script type="text/javascript">
14366  */
14367
14368  
14369 /**
14370  * @class Roo.KeyMap
14371  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14372  * The constructor accepts the same config object as defined by {@link #addBinding}.
14373  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14374  * combination it will call the function with this signature (if the match is a multi-key
14375  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14376  * A KeyMap can also handle a string representation of keys.<br />
14377  * Usage:
14378  <pre><code>
14379 // map one key by key code
14380 var map = new Roo.KeyMap("my-element", {
14381     key: 13, // or Roo.EventObject.ENTER
14382     fn: myHandler,
14383     scope: myObject
14384 });
14385
14386 // map multiple keys to one action by string
14387 var map = new Roo.KeyMap("my-element", {
14388     key: "a\r\n\t",
14389     fn: myHandler,
14390     scope: myObject
14391 });
14392
14393 // map multiple keys to multiple actions by strings and array of codes
14394 var map = new Roo.KeyMap("my-element", [
14395     {
14396         key: [10,13],
14397         fn: function(){ alert("Return was pressed"); }
14398     }, {
14399         key: "abc",
14400         fn: function(){ alert('a, b or c was pressed'); }
14401     }, {
14402         key: "\t",
14403         ctrl:true,
14404         shift:true,
14405         fn: function(){ alert('Control + shift + tab was pressed.'); }
14406     }
14407 ]);
14408 </code></pre>
14409  * <b>Note: A KeyMap starts enabled</b>
14410  * @constructor
14411  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14412  * @param {Object} config The config (see {@link #addBinding})
14413  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14414  */
14415 Roo.KeyMap = function(el, config, eventName){
14416     this.el  = Roo.get(el);
14417     this.eventName = eventName || "keydown";
14418     this.bindings = [];
14419     if(config){
14420         this.addBinding(config);
14421     }
14422     this.enable();
14423 };
14424
14425 Roo.KeyMap.prototype = {
14426     /**
14427      * True to stop the event from bubbling and prevent the default browser action if the
14428      * key was handled by the KeyMap (defaults to false)
14429      * @type Boolean
14430      */
14431     stopEvent : false,
14432
14433     /**
14434      * Add a new binding to this KeyMap. The following config object properties are supported:
14435      * <pre>
14436 Property    Type             Description
14437 ----------  ---------------  ----------------------------------------------------------------------
14438 key         String/Array     A single keycode or an array of keycodes to handle
14439 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14440 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14441 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14442 fn          Function         The function to call when KeyMap finds the expected key combination
14443 scope       Object           The scope of the callback function
14444 </pre>
14445      *
14446      * Usage:
14447      * <pre><code>
14448 // Create a KeyMap
14449 var map = new Roo.KeyMap(document, {
14450     key: Roo.EventObject.ENTER,
14451     fn: handleKey,
14452     scope: this
14453 });
14454
14455 //Add a new binding to the existing KeyMap later
14456 map.addBinding({
14457     key: 'abc',
14458     shift: true,
14459     fn: handleKey,
14460     scope: this
14461 });
14462 </code></pre>
14463      * @param {Object/Array} config A single KeyMap config or an array of configs
14464      */
14465         addBinding : function(config){
14466         if(config instanceof Array){
14467             for(var i = 0, len = config.length; i < len; i++){
14468                 this.addBinding(config[i]);
14469             }
14470             return;
14471         }
14472         var keyCode = config.key,
14473             shift = config.shift, 
14474             ctrl = config.ctrl, 
14475             alt = config.alt,
14476             fn = config.fn,
14477             scope = config.scope;
14478         if(typeof keyCode == "string"){
14479             var ks = [];
14480             var keyString = keyCode.toUpperCase();
14481             for(var j = 0, len = keyString.length; j < len; j++){
14482                 ks.push(keyString.charCodeAt(j));
14483             }
14484             keyCode = ks;
14485         }
14486         var keyArray = keyCode instanceof Array;
14487         var handler = function(e){
14488             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14489                 var k = e.getKey();
14490                 if(keyArray){
14491                     for(var i = 0, len = keyCode.length; i < len; i++){
14492                         if(keyCode[i] == k){
14493                           if(this.stopEvent){
14494                               e.stopEvent();
14495                           }
14496                           fn.call(scope || window, k, e);
14497                           return;
14498                         }
14499                     }
14500                 }else{
14501                     if(k == keyCode){
14502                         if(this.stopEvent){
14503                            e.stopEvent();
14504                         }
14505                         fn.call(scope || window, k, e);
14506                     }
14507                 }
14508             }
14509         };
14510         this.bindings.push(handler);  
14511         },
14512
14513     /**
14514      * Shorthand for adding a single key listener
14515      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14516      * following options:
14517      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14518      * @param {Function} fn The function to call
14519      * @param {Object} scope (optional) The scope of the function
14520      */
14521     on : function(key, fn, scope){
14522         var keyCode, shift, ctrl, alt;
14523         if(typeof key == "object" && !(key instanceof Array)){
14524             keyCode = key.key;
14525             shift = key.shift;
14526             ctrl = key.ctrl;
14527             alt = key.alt;
14528         }else{
14529             keyCode = key;
14530         }
14531         this.addBinding({
14532             key: keyCode,
14533             shift: shift,
14534             ctrl: ctrl,
14535             alt: alt,
14536             fn: fn,
14537             scope: scope
14538         })
14539     },
14540
14541     // private
14542     handleKeyDown : function(e){
14543             if(this.enabled){ //just in case
14544             var b = this.bindings;
14545             for(var i = 0, len = b.length; i < len; i++){
14546                 b[i].call(this, e);
14547             }
14548             }
14549         },
14550         
14551         /**
14552          * Returns true if this KeyMap is enabled
14553          * @return {Boolean} 
14554          */
14555         isEnabled : function(){
14556             return this.enabled;  
14557         },
14558         
14559         /**
14560          * Enables this KeyMap
14561          */
14562         enable: function(){
14563                 if(!this.enabled){
14564                     this.el.on(this.eventName, this.handleKeyDown, this);
14565                     this.enabled = true;
14566                 }
14567         },
14568
14569         /**
14570          * Disable this KeyMap
14571          */
14572         disable: function(){
14573                 if(this.enabled){
14574                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14575                     this.enabled = false;
14576                 }
14577         }
14578 };/*
14579  * Based on:
14580  * Ext JS Library 1.1.1
14581  * Copyright(c) 2006-2007, Ext JS, LLC.
14582  *
14583  * Originally Released Under LGPL - original licence link has changed is not relivant.
14584  *
14585  * Fork - LGPL
14586  * <script type="text/javascript">
14587  */
14588
14589  
14590 /**
14591  * @class Roo.util.TextMetrics
14592  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14593  * wide, in pixels, a given block of text will be.
14594  * @singleton
14595  */
14596 Roo.util.TextMetrics = function(){
14597     var shared;
14598     return {
14599         /**
14600          * Measures the size of the specified text
14601          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14602          * that can affect the size of the rendered text
14603          * @param {String} text The text to measure
14604          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14605          * in order to accurately measure the text height
14606          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14607          */
14608         measure : function(el, text, fixedWidth){
14609             if(!shared){
14610                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14611             }
14612             shared.bind(el);
14613             shared.setFixedWidth(fixedWidth || 'auto');
14614             return shared.getSize(text);
14615         },
14616
14617         /**
14618          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14619          * the overhead of multiple calls to initialize the style properties on each measurement.
14620          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14621          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14622          * in order to accurately measure the text height
14623          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14624          */
14625         createInstance : function(el, fixedWidth){
14626             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14627         }
14628     };
14629 }();
14630
14631  
14632
14633 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14634     var ml = new Roo.Element(document.createElement('div'));
14635     document.body.appendChild(ml.dom);
14636     ml.position('absolute');
14637     ml.setLeftTop(-1000, -1000);
14638     ml.hide();
14639
14640     if(fixedWidth){
14641         ml.setWidth(fixedWidth);
14642     }
14643      
14644     var instance = {
14645         /**
14646          * Returns the size of the specified text based on the internal element's style and width properties
14647          * @memberOf Roo.util.TextMetrics.Instance#
14648          * @param {String} text The text to measure
14649          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14650          */
14651         getSize : function(text){
14652             ml.update(text);
14653             var s = ml.getSize();
14654             ml.update('');
14655             return s;
14656         },
14657
14658         /**
14659          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14660          * that can affect the size of the rendered text
14661          * @memberOf Roo.util.TextMetrics.Instance#
14662          * @param {String/HTMLElement} el The element, dom node or id
14663          */
14664         bind : function(el){
14665             ml.setStyle(
14666                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14667             );
14668         },
14669
14670         /**
14671          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14672          * to set a fixed width in order to accurately measure the text height.
14673          * @memberOf Roo.util.TextMetrics.Instance#
14674          * @param {Number} width The width to set on the element
14675          */
14676         setFixedWidth : function(width){
14677             ml.setWidth(width);
14678         },
14679
14680         /**
14681          * Returns the measured width of the specified text
14682          * @memberOf Roo.util.TextMetrics.Instance#
14683          * @param {String} text The text to measure
14684          * @return {Number} width The width in pixels
14685          */
14686         getWidth : function(text){
14687             ml.dom.style.width = 'auto';
14688             return this.getSize(text).width;
14689         },
14690
14691         /**
14692          * Returns the measured height of the specified text.  For multiline text, be sure to call
14693          * {@link #setFixedWidth} if necessary.
14694          * @memberOf Roo.util.TextMetrics.Instance#
14695          * @param {String} text The text to measure
14696          * @return {Number} height The height in pixels
14697          */
14698         getHeight : function(text){
14699             return this.getSize(text).height;
14700         }
14701     };
14702
14703     instance.bind(bindTo);
14704
14705     return instance;
14706 };
14707
14708 // backwards compat
14709 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14710  * Based on:
14711  * Ext JS Library 1.1.1
14712  * Copyright(c) 2006-2007, Ext JS, LLC.
14713  *
14714  * Originally Released Under LGPL - original licence link has changed is not relivant.
14715  *
14716  * Fork - LGPL
14717  * <script type="text/javascript">
14718  */
14719
14720 /**
14721  * @class Roo.state.Provider
14722  * Abstract base class for state provider implementations. This class provides methods
14723  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14724  * Provider interface.
14725  */
14726 Roo.state.Provider = function(){
14727     /**
14728      * @event statechange
14729      * Fires when a state change occurs.
14730      * @param {Provider} this This state provider
14731      * @param {String} key The state key which was changed
14732      * @param {String} value The encoded value for the state
14733      */
14734     this.addEvents({
14735         "statechange": true
14736     });
14737     this.state = {};
14738     Roo.state.Provider.superclass.constructor.call(this);
14739 };
14740 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14741     /**
14742      * Returns the current value for a key
14743      * @param {String} name The key name
14744      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14745      * @return {Mixed} The state data
14746      */
14747     get : function(name, defaultValue){
14748         return typeof this.state[name] == "undefined" ?
14749             defaultValue : this.state[name];
14750     },
14751     
14752     /**
14753      * Clears a value from the state
14754      * @param {String} name The key name
14755      */
14756     clear : function(name){
14757         delete this.state[name];
14758         this.fireEvent("statechange", this, name, null);
14759     },
14760     
14761     /**
14762      * Sets the value for a key
14763      * @param {String} name The key name
14764      * @param {Mixed} value The value to set
14765      */
14766     set : function(name, value){
14767         this.state[name] = value;
14768         this.fireEvent("statechange", this, name, value);
14769     },
14770     
14771     /**
14772      * Decodes a string previously encoded with {@link #encodeValue}.
14773      * @param {String} value The value to decode
14774      * @return {Mixed} The decoded value
14775      */
14776     decodeValue : function(cookie){
14777         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14778         var matches = re.exec(unescape(cookie));
14779         if(!matches || !matches[1]) return; // non state cookie
14780         var type = matches[1];
14781         var v = matches[2];
14782         switch(type){
14783             case "n":
14784                 return parseFloat(v);
14785             case "d":
14786                 return new Date(Date.parse(v));
14787             case "b":
14788                 return (v == "1");
14789             case "a":
14790                 var all = [];
14791                 var values = v.split("^");
14792                 for(var i = 0, len = values.length; i < len; i++){
14793                     all.push(this.decodeValue(values[i]));
14794                 }
14795                 return all;
14796            case "o":
14797                 var all = {};
14798                 var values = v.split("^");
14799                 for(var i = 0, len = values.length; i < len; i++){
14800                     var kv = values[i].split("=");
14801                     all[kv[0]] = this.decodeValue(kv[1]);
14802                 }
14803                 return all;
14804            default:
14805                 return v;
14806         }
14807     },
14808     
14809     /**
14810      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14811      * @param {Mixed} value The value to encode
14812      * @return {String} The encoded value
14813      */
14814     encodeValue : function(v){
14815         var enc;
14816         if(typeof v == "number"){
14817             enc = "n:" + v;
14818         }else if(typeof v == "boolean"){
14819             enc = "b:" + (v ? "1" : "0");
14820         }else if(v instanceof Date){
14821             enc = "d:" + v.toGMTString();
14822         }else if(v instanceof Array){
14823             var flat = "";
14824             for(var i = 0, len = v.length; i < len; i++){
14825                 flat += this.encodeValue(v[i]);
14826                 if(i != len-1) flat += "^";
14827             }
14828             enc = "a:" + flat;
14829         }else if(typeof v == "object"){
14830             var flat = "";
14831             for(var key in v){
14832                 if(typeof v[key] != "function"){
14833                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14834                 }
14835             }
14836             enc = "o:" + flat.substring(0, flat.length-1);
14837         }else{
14838             enc = "s:" + v;
14839         }
14840         return escape(enc);        
14841     }
14842 });
14843
14844 /*
14845  * Based on:
14846  * Ext JS Library 1.1.1
14847  * Copyright(c) 2006-2007, Ext JS, LLC.
14848  *
14849  * Originally Released Under LGPL - original licence link has changed is not relivant.
14850  *
14851  * Fork - LGPL
14852  * <script type="text/javascript">
14853  */
14854 /**
14855  * @class Roo.state.Manager
14856  * This is the global state manager. By default all components that are "state aware" check this class
14857  * for state information if you don't pass them a custom state provider. In order for this class
14858  * to be useful, it must be initialized with a provider when your application initializes.
14859  <pre><code>
14860 // in your initialization function
14861 init : function(){
14862    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14863    ...
14864    // supposed you have a {@link Roo.BorderLayout}
14865    var layout = new Roo.BorderLayout(...);
14866    layout.restoreState();
14867    // or a {Roo.BasicDialog}
14868    var dialog = new Roo.BasicDialog(...);
14869    dialog.restoreState();
14870  </code></pre>
14871  * @singleton
14872  */
14873 Roo.state.Manager = function(){
14874     var provider = new Roo.state.Provider();
14875     
14876     return {
14877         /**
14878          * Configures the default state provider for your application
14879          * @param {Provider} stateProvider The state provider to set
14880          */
14881         setProvider : function(stateProvider){
14882             provider = stateProvider;
14883         },
14884         
14885         /**
14886          * Returns the current value for a key
14887          * @param {String} name The key name
14888          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14889          * @return {Mixed} The state data
14890          */
14891         get : function(key, defaultValue){
14892             return provider.get(key, defaultValue);
14893         },
14894         
14895         /**
14896          * Sets the value for a key
14897          * @param {String} name The key name
14898          * @param {Mixed} value The state data
14899          */
14900          set : function(key, value){
14901             provider.set(key, value);
14902         },
14903         
14904         /**
14905          * Clears a value from the state
14906          * @param {String} name The key name
14907          */
14908         clear : function(key){
14909             provider.clear(key);
14910         },
14911         
14912         /**
14913          * Gets the currently configured state provider
14914          * @return {Provider} The state provider
14915          */
14916         getProvider : function(){
14917             return provider;
14918         }
14919     };
14920 }();
14921 /*
14922  * Based on:
14923  * Ext JS Library 1.1.1
14924  * Copyright(c) 2006-2007, Ext JS, LLC.
14925  *
14926  * Originally Released Under LGPL - original licence link has changed is not relivant.
14927  *
14928  * Fork - LGPL
14929  * <script type="text/javascript">
14930  */
14931 /**
14932  * @class Roo.state.CookieProvider
14933  * @extends Roo.state.Provider
14934  * The default Provider implementation which saves state via cookies.
14935  * <br />Usage:
14936  <pre><code>
14937    var cp = new Roo.state.CookieProvider({
14938        path: "/cgi-bin/",
14939        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14940        domain: "roojs.com"
14941    })
14942    Roo.state.Manager.setProvider(cp);
14943  </code></pre>
14944  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14945  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14946  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14947  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14948  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14949  * domain the page is running on including the 'www' like 'www.roojs.com')
14950  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14951  * @constructor
14952  * Create a new CookieProvider
14953  * @param {Object} config The configuration object
14954  */
14955 Roo.state.CookieProvider = function(config){
14956     Roo.state.CookieProvider.superclass.constructor.call(this);
14957     this.path = "/";
14958     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14959     this.domain = null;
14960     this.secure = false;
14961     Roo.apply(this, config);
14962     this.state = this.readCookies();
14963 };
14964
14965 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14966     // private
14967     set : function(name, value){
14968         if(typeof value == "undefined" || value === null){
14969             this.clear(name);
14970             return;
14971         }
14972         this.setCookie(name, value);
14973         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14974     },
14975
14976     // private
14977     clear : function(name){
14978         this.clearCookie(name);
14979         Roo.state.CookieProvider.superclass.clear.call(this, name);
14980     },
14981
14982     // private
14983     readCookies : function(){
14984         var cookies = {};
14985         var c = document.cookie + ";";
14986         var re = /\s?(.*?)=(.*?);/g;
14987         var matches;
14988         while((matches = re.exec(c)) != null){
14989             var name = matches[1];
14990             var value = matches[2];
14991             if(name && name.substring(0,3) == "ys-"){
14992                 cookies[name.substr(3)] = this.decodeValue(value);
14993             }
14994         }
14995         return cookies;
14996     },
14997
14998     // private
14999     setCookie : function(name, value){
15000         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15001            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15002            ((this.path == null) ? "" : ("; path=" + this.path)) +
15003            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15004            ((this.secure == true) ? "; secure" : "");
15005     },
15006
15007     // private
15008     clearCookie : function(name){
15009         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15010            ((this.path == null) ? "" : ("; path=" + this.path)) +
15011            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15012            ((this.secure == true) ? "; secure" : "");
15013     }
15014 });/*
15015  * Based on:
15016  * Ext JS Library 1.1.1
15017  * Copyright(c) 2006-2007, Ext JS, LLC.
15018  *
15019  * Originally Released Under LGPL - original licence link has changed is not relivant.
15020  *
15021  * Fork - LGPL
15022  * <script type="text/javascript">
15023  */
15024  
15025
15026 /**
15027  * @class Roo.ComponentMgr
15028  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15029  * @singleton
15030  */
15031 Roo.ComponentMgr = function(){
15032     var all = new Roo.util.MixedCollection();
15033
15034     return {
15035         /**
15036          * Registers a component.
15037          * @param {Roo.Component} c The component
15038          */
15039         register : function(c){
15040             all.add(c);
15041         },
15042
15043         /**
15044          * Unregisters a component.
15045          * @param {Roo.Component} c The component
15046          */
15047         unregister : function(c){
15048             all.remove(c);
15049         },
15050
15051         /**
15052          * Returns a component by id
15053          * @param {String} id The component id
15054          */
15055         get : function(id){
15056             return all.get(id);
15057         },
15058
15059         /**
15060          * Registers a function that will be called when a specified component is added to ComponentMgr
15061          * @param {String} id The component id
15062          * @param {Funtction} fn The callback function
15063          * @param {Object} scope The scope of the callback
15064          */
15065         onAvailable : function(id, fn, scope){
15066             all.on("add", function(index, o){
15067                 if(o.id == id){
15068                     fn.call(scope || o, o);
15069                     all.un("add", fn, scope);
15070                 }
15071             });
15072         }
15073     };
15074 }();/*
15075  * Based on:
15076  * Ext JS Library 1.1.1
15077  * Copyright(c) 2006-2007, Ext JS, LLC.
15078  *
15079  * Originally Released Under LGPL - original licence link has changed is not relivant.
15080  *
15081  * Fork - LGPL
15082  * <script type="text/javascript">
15083  */
15084  
15085 /**
15086  * @class Roo.Component
15087  * @extends Roo.util.Observable
15088  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15089  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15090  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15091  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15092  * All visual components (widgets) that require rendering into a layout should subclass Component.
15093  * @constructor
15094  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15095  * 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
15096  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15097  */
15098 Roo.Component = function(config){
15099     config = config || {};
15100     if(config.tagName || config.dom || typeof config == "string"){ // element object
15101         config = {el: config, id: config.id || config};
15102     }
15103     this.initialConfig = config;
15104
15105     Roo.apply(this, config);
15106     this.addEvents({
15107         /**
15108          * @event disable
15109          * Fires after the component is disabled.
15110              * @param {Roo.Component} this
15111              */
15112         disable : true,
15113         /**
15114          * @event enable
15115          * Fires after the component is enabled.
15116              * @param {Roo.Component} this
15117              */
15118         enable : true,
15119         /**
15120          * @event beforeshow
15121          * Fires before the component is shown.  Return false to stop the show.
15122              * @param {Roo.Component} this
15123              */
15124         beforeshow : true,
15125         /**
15126          * @event show
15127          * Fires after the component is shown.
15128              * @param {Roo.Component} this
15129              */
15130         show : true,
15131         /**
15132          * @event beforehide
15133          * Fires before the component is hidden. Return false to stop the hide.
15134              * @param {Roo.Component} this
15135              */
15136         beforehide : true,
15137         /**
15138          * @event hide
15139          * Fires after the component is hidden.
15140              * @param {Roo.Component} this
15141              */
15142         hide : true,
15143         /**
15144          * @event beforerender
15145          * Fires before the component is rendered. Return false to stop the render.
15146              * @param {Roo.Component} this
15147              */
15148         beforerender : true,
15149         /**
15150          * @event render
15151          * Fires after the component is rendered.
15152              * @param {Roo.Component} this
15153              */
15154         render : true,
15155         /**
15156          * @event beforedestroy
15157          * Fires before the component is destroyed. Return false to stop the destroy.
15158              * @param {Roo.Component} this
15159              */
15160         beforedestroy : true,
15161         /**
15162          * @event destroy
15163          * Fires after the component is destroyed.
15164              * @param {Roo.Component} this
15165              */
15166         destroy : true
15167     });
15168     if(!this.id){
15169         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15170     }
15171     Roo.ComponentMgr.register(this);
15172     Roo.Component.superclass.constructor.call(this);
15173     this.initComponent();
15174     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15175         this.render(this.renderTo);
15176         delete this.renderTo;
15177     }
15178 };
15179
15180 /** @private */
15181 Roo.Component.AUTO_ID = 1000;
15182
15183 Roo.extend(Roo.Component, Roo.util.Observable, {
15184     /**
15185      * @scope Roo.Component.prototype
15186      * @type {Boolean}
15187      * true if this component is hidden. Read-only.
15188      */
15189     hidden : false,
15190     /**
15191      * @type {Boolean}
15192      * true if this component is disabled. Read-only.
15193      */
15194     disabled : false,
15195     /**
15196      * @type {Boolean}
15197      * true if this component has been rendered. Read-only.
15198      */
15199     rendered : false,
15200     
15201     /** @cfg {String} disableClass
15202      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15203      */
15204     disabledClass : "x-item-disabled",
15205         /** @cfg {Boolean} allowDomMove
15206          * Whether the component can move the Dom node when rendering (defaults to true).
15207          */
15208     allowDomMove : true,
15209     /** @cfg {String} hideMode (display|visibility)
15210      * How this component should hidden. Supported values are
15211      * "visibility" (css visibility), "offsets" (negative offset position) and
15212      * "display" (css display) - defaults to "display".
15213      */
15214     hideMode: 'display',
15215
15216     /** @private */
15217     ctype : "Roo.Component",
15218
15219     /**
15220      * @cfg {String} actionMode 
15221      * which property holds the element that used for  hide() / show() / disable() / enable()
15222      * default is 'el' 
15223      */
15224     actionMode : "el",
15225
15226     /** @private */
15227     getActionEl : function(){
15228         return this[this.actionMode];
15229     },
15230
15231     initComponent : Roo.emptyFn,
15232     /**
15233      * If this is a lazy rendering component, render it to its container element.
15234      * @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.
15235      */
15236     render : function(container, position){
15237         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15238             if(!container && this.el){
15239                 this.el = Roo.get(this.el);
15240                 container = this.el.dom.parentNode;
15241                 this.allowDomMove = false;
15242             }
15243             this.container = Roo.get(container);
15244             this.rendered = true;
15245             if(position !== undefined){
15246                 if(typeof position == 'number'){
15247                     position = this.container.dom.childNodes[position];
15248                 }else{
15249                     position = Roo.getDom(position);
15250                 }
15251             }
15252             this.onRender(this.container, position || null);
15253             if(this.cls){
15254                 this.el.addClass(this.cls);
15255                 delete this.cls;
15256             }
15257             if(this.style){
15258                 this.el.applyStyles(this.style);
15259                 delete this.style;
15260             }
15261             this.fireEvent("render", this);
15262             this.afterRender(this.container);
15263             if(this.hidden){
15264                 this.hide();
15265             }
15266             if(this.disabled){
15267                 this.disable();
15268             }
15269         }
15270         return this;
15271     },
15272
15273     /** @private */
15274     // default function is not really useful
15275     onRender : function(ct, position){
15276         if(this.el){
15277             this.el = Roo.get(this.el);
15278             if(this.allowDomMove !== false){
15279                 ct.dom.insertBefore(this.el.dom, position);
15280             }
15281         }
15282     },
15283
15284     /** @private */
15285     getAutoCreate : function(){
15286         var cfg = typeof this.autoCreate == "object" ?
15287                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15288         if(this.id && !cfg.id){
15289             cfg.id = this.id;
15290         }
15291         return cfg;
15292     },
15293
15294     /** @private */
15295     afterRender : Roo.emptyFn,
15296
15297     /**
15298      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15299      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15300      */
15301     destroy : function(){
15302         if(this.fireEvent("beforedestroy", this) !== false){
15303             this.purgeListeners();
15304             this.beforeDestroy();
15305             if(this.rendered){
15306                 this.el.removeAllListeners();
15307                 this.el.remove();
15308                 if(this.actionMode == "container"){
15309                     this.container.remove();
15310                 }
15311             }
15312             this.onDestroy();
15313             Roo.ComponentMgr.unregister(this);
15314             this.fireEvent("destroy", this);
15315         }
15316     },
15317
15318         /** @private */
15319     beforeDestroy : function(){
15320
15321     },
15322
15323         /** @private */
15324         onDestroy : function(){
15325
15326     },
15327
15328     /**
15329      * Returns the underlying {@link Roo.Element}.
15330      * @return {Roo.Element} The element
15331      */
15332     getEl : function(){
15333         return this.el;
15334     },
15335
15336     /**
15337      * Returns the id of this component.
15338      * @return {String}
15339      */
15340     getId : function(){
15341         return this.id;
15342     },
15343
15344     /**
15345      * Try to focus this component.
15346      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15347      * @return {Roo.Component} this
15348      */
15349     focus : function(selectText){
15350         if(this.rendered){
15351             this.el.focus();
15352             if(selectText === true){
15353                 this.el.dom.select();
15354             }
15355         }
15356         return this;
15357     },
15358
15359     /** @private */
15360     blur : function(){
15361         if(this.rendered){
15362             this.el.blur();
15363         }
15364         return this;
15365     },
15366
15367     /**
15368      * Disable this component.
15369      * @return {Roo.Component} this
15370      */
15371     disable : function(){
15372         if(this.rendered){
15373             this.onDisable();
15374         }
15375         this.disabled = true;
15376         this.fireEvent("disable", this);
15377         return this;
15378     },
15379
15380         // private
15381     onDisable : function(){
15382         this.getActionEl().addClass(this.disabledClass);
15383         this.el.dom.disabled = true;
15384     },
15385
15386     /**
15387      * Enable this component.
15388      * @return {Roo.Component} this
15389      */
15390     enable : function(){
15391         if(this.rendered){
15392             this.onEnable();
15393         }
15394         this.disabled = false;
15395         this.fireEvent("enable", this);
15396         return this;
15397     },
15398
15399         // private
15400     onEnable : function(){
15401         this.getActionEl().removeClass(this.disabledClass);
15402         this.el.dom.disabled = false;
15403     },
15404
15405     /**
15406      * Convenience function for setting disabled/enabled by boolean.
15407      * @param {Boolean} disabled
15408      */
15409     setDisabled : function(disabled){
15410         this[disabled ? "disable" : "enable"]();
15411     },
15412
15413     /**
15414      * Show this component.
15415      * @return {Roo.Component} this
15416      */
15417     show: function(){
15418         if(this.fireEvent("beforeshow", this) !== false){
15419             this.hidden = false;
15420             if(this.rendered){
15421                 this.onShow();
15422             }
15423             this.fireEvent("show", this);
15424         }
15425         return this;
15426     },
15427
15428     // private
15429     onShow : function(){
15430         var ae = this.getActionEl();
15431         if(this.hideMode == 'visibility'){
15432             ae.dom.style.visibility = "visible";
15433         }else if(this.hideMode == 'offsets'){
15434             ae.removeClass('x-hidden');
15435         }else{
15436             ae.dom.style.display = "";
15437         }
15438     },
15439
15440     /**
15441      * Hide this component.
15442      * @return {Roo.Component} this
15443      */
15444     hide: function(){
15445         if(this.fireEvent("beforehide", this) !== false){
15446             this.hidden = true;
15447             if(this.rendered){
15448                 this.onHide();
15449             }
15450             this.fireEvent("hide", this);
15451         }
15452         return this;
15453     },
15454
15455     // private
15456     onHide : function(){
15457         var ae = this.getActionEl();
15458         if(this.hideMode == 'visibility'){
15459             ae.dom.style.visibility = "hidden";
15460         }else if(this.hideMode == 'offsets'){
15461             ae.addClass('x-hidden');
15462         }else{
15463             ae.dom.style.display = "none";
15464         }
15465     },
15466
15467     /**
15468      * Convenience function to hide or show this component by boolean.
15469      * @param {Boolean} visible True to show, false to hide
15470      * @return {Roo.Component} this
15471      */
15472     setVisible: function(visible){
15473         if(visible) {
15474             this.show();
15475         }else{
15476             this.hide();
15477         }
15478         return this;
15479     },
15480
15481     /**
15482      * Returns true if this component is visible.
15483      */
15484     isVisible : function(){
15485         return this.getActionEl().isVisible();
15486     },
15487
15488     cloneConfig : function(overrides){
15489         overrides = overrides || {};
15490         var id = overrides.id || Roo.id();
15491         var cfg = Roo.applyIf(overrides, this.initialConfig);
15492         cfg.id = id; // prevent dup id
15493         return new this.constructor(cfg);
15494     }
15495 });/*
15496  * Based on:
15497  * Ext JS Library 1.1.1
15498  * Copyright(c) 2006-2007, Ext JS, LLC.
15499  *
15500  * Originally Released Under LGPL - original licence link has changed is not relivant.
15501  *
15502  * Fork - LGPL
15503  * <script type="text/javascript">
15504  */
15505
15506 /**
15507  * @class Roo.BoxComponent
15508  * @extends Roo.Component
15509  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15510  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15511  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15512  * layout containers.
15513  * @constructor
15514  * @param {Roo.Element/String/Object} config The configuration options.
15515  */
15516 Roo.BoxComponent = function(config){
15517     Roo.Component.call(this, config);
15518     this.addEvents({
15519         /**
15520          * @event resize
15521          * Fires after the component is resized.
15522              * @param {Roo.Component} this
15523              * @param {Number} adjWidth The box-adjusted width that was set
15524              * @param {Number} adjHeight The box-adjusted height that was set
15525              * @param {Number} rawWidth The width that was originally specified
15526              * @param {Number} rawHeight The height that was originally specified
15527              */
15528         resize : true,
15529         /**
15530          * @event move
15531          * Fires after the component is moved.
15532              * @param {Roo.Component} this
15533              * @param {Number} x The new x position
15534              * @param {Number} y The new y position
15535              */
15536         move : true
15537     });
15538 };
15539
15540 Roo.extend(Roo.BoxComponent, Roo.Component, {
15541     // private, set in afterRender to signify that the component has been rendered
15542     boxReady : false,
15543     // private, used to defer height settings to subclasses
15544     deferHeight: false,
15545     /** @cfg {Number} width
15546      * width (optional) size of component
15547      */
15548      /** @cfg {Number} height
15549      * height (optional) size of component
15550      */
15551      
15552     /**
15553      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15554      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15555      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15556      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15557      * @return {Roo.BoxComponent} this
15558      */
15559     setSize : function(w, h){
15560         // support for standard size objects
15561         if(typeof w == 'object'){
15562             h = w.height;
15563             w = w.width;
15564         }
15565         // not rendered
15566         if(!this.boxReady){
15567             this.width = w;
15568             this.height = h;
15569             return this;
15570         }
15571
15572         // prevent recalcs when not needed
15573         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15574             return this;
15575         }
15576         this.lastSize = {width: w, height: h};
15577
15578         var adj = this.adjustSize(w, h);
15579         var aw = adj.width, ah = adj.height;
15580         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15581             var rz = this.getResizeEl();
15582             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15583                 rz.setSize(aw, ah);
15584             }else if(!this.deferHeight && ah !== undefined){
15585                 rz.setHeight(ah);
15586             }else if(aw !== undefined){
15587                 rz.setWidth(aw);
15588             }
15589             this.onResize(aw, ah, w, h);
15590             this.fireEvent('resize', this, aw, ah, w, h);
15591         }
15592         return this;
15593     },
15594
15595     /**
15596      * Gets the current size of the component's underlying element.
15597      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15598      */
15599     getSize : function(){
15600         return this.el.getSize();
15601     },
15602
15603     /**
15604      * Gets the current XY position of the component's underlying element.
15605      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15606      * @return {Array} The XY position of the element (e.g., [100, 200])
15607      */
15608     getPosition : function(local){
15609         if(local === true){
15610             return [this.el.getLeft(true), this.el.getTop(true)];
15611         }
15612         return this.xy || this.el.getXY();
15613     },
15614
15615     /**
15616      * Gets the current box measurements of the component's underlying element.
15617      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15618      * @returns {Object} box An object in the format {x, y, width, height}
15619      */
15620     getBox : function(local){
15621         var s = this.el.getSize();
15622         if(local){
15623             s.x = this.el.getLeft(true);
15624             s.y = this.el.getTop(true);
15625         }else{
15626             var xy = this.xy || this.el.getXY();
15627             s.x = xy[0];
15628             s.y = xy[1];
15629         }
15630         return s;
15631     },
15632
15633     /**
15634      * Sets the current box measurements of the component's underlying element.
15635      * @param {Object} box An object in the format {x, y, width, height}
15636      * @returns {Roo.BoxComponent} this
15637      */
15638     updateBox : function(box){
15639         this.setSize(box.width, box.height);
15640         this.setPagePosition(box.x, box.y);
15641         return this;
15642     },
15643
15644     // protected
15645     getResizeEl : function(){
15646         return this.resizeEl || this.el;
15647     },
15648
15649     // protected
15650     getPositionEl : function(){
15651         return this.positionEl || this.el;
15652     },
15653
15654     /**
15655      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15656      * This method fires the move event.
15657      * @param {Number} left The new left
15658      * @param {Number} top The new top
15659      * @returns {Roo.BoxComponent} this
15660      */
15661     setPosition : function(x, y){
15662         this.x = x;
15663         this.y = y;
15664         if(!this.boxReady){
15665             return this;
15666         }
15667         var adj = this.adjustPosition(x, y);
15668         var ax = adj.x, ay = adj.y;
15669
15670         var el = this.getPositionEl();
15671         if(ax !== undefined || ay !== undefined){
15672             if(ax !== undefined && ay !== undefined){
15673                 el.setLeftTop(ax, ay);
15674             }else if(ax !== undefined){
15675                 el.setLeft(ax);
15676             }else if(ay !== undefined){
15677                 el.setTop(ay);
15678             }
15679             this.onPosition(ax, ay);
15680             this.fireEvent('move', this, ax, ay);
15681         }
15682         return this;
15683     },
15684
15685     /**
15686      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15687      * This method fires the move event.
15688      * @param {Number} x The new x position
15689      * @param {Number} y The new y position
15690      * @returns {Roo.BoxComponent} this
15691      */
15692     setPagePosition : function(x, y){
15693         this.pageX = x;
15694         this.pageY = y;
15695         if(!this.boxReady){
15696             return;
15697         }
15698         if(x === undefined || y === undefined){ // cannot translate undefined points
15699             return;
15700         }
15701         var p = this.el.translatePoints(x, y);
15702         this.setPosition(p.left, p.top);
15703         return this;
15704     },
15705
15706     // private
15707     onRender : function(ct, position){
15708         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15709         if(this.resizeEl){
15710             this.resizeEl = Roo.get(this.resizeEl);
15711         }
15712         if(this.positionEl){
15713             this.positionEl = Roo.get(this.positionEl);
15714         }
15715     },
15716
15717     // private
15718     afterRender : function(){
15719         Roo.BoxComponent.superclass.afterRender.call(this);
15720         this.boxReady = true;
15721         this.setSize(this.width, this.height);
15722         if(this.x || this.y){
15723             this.setPosition(this.x, this.y);
15724         }
15725         if(this.pageX || this.pageY){
15726             this.setPagePosition(this.pageX, this.pageY);
15727         }
15728     },
15729
15730     /**
15731      * Force the component's size to recalculate based on the underlying element's current height and width.
15732      * @returns {Roo.BoxComponent} this
15733      */
15734     syncSize : function(){
15735         delete this.lastSize;
15736         this.setSize(this.el.getWidth(), this.el.getHeight());
15737         return this;
15738     },
15739
15740     /**
15741      * Called after the component is resized, this method is empty by default but can be implemented by any
15742      * subclass that needs to perform custom logic after a resize occurs.
15743      * @param {Number} adjWidth The box-adjusted width that was set
15744      * @param {Number} adjHeight The box-adjusted height that was set
15745      * @param {Number} rawWidth The width that was originally specified
15746      * @param {Number} rawHeight The height that was originally specified
15747      */
15748     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15749
15750     },
15751
15752     /**
15753      * Called after the component is moved, this method is empty by default but can be implemented by any
15754      * subclass that needs to perform custom logic after a move occurs.
15755      * @param {Number} x The new x position
15756      * @param {Number} y The new y position
15757      */
15758     onPosition : function(x, y){
15759
15760     },
15761
15762     // private
15763     adjustSize : function(w, h){
15764         if(this.autoWidth){
15765             w = 'auto';
15766         }
15767         if(this.autoHeight){
15768             h = 'auto';
15769         }
15770         return {width : w, height: h};
15771     },
15772
15773     // private
15774     adjustPosition : function(x, y){
15775         return {x : x, y: y};
15776     }
15777 });/*
15778  * Original code for Roojs - LGPL
15779  * <script type="text/javascript">
15780  */
15781  
15782 /**
15783  * @class Roo.XComponent
15784  * A delayed Element creator...
15785  * Or a way to group chunks of interface together.
15786  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15787  *  used in conjunction with XComponent.build() it will create an instance of each element,
15788  *  then call addxtype() to build the User interface.
15789  * 
15790  * Mypart.xyx = new Roo.XComponent({
15791
15792     parent : 'Mypart.xyz', // empty == document.element.!!
15793     order : '001',
15794     name : 'xxxx'
15795     region : 'xxxx'
15796     disabled : function() {} 
15797      
15798     tree : function() { // return an tree of xtype declared components
15799         var MODULE = this;
15800         return 
15801         {
15802             xtype : 'NestedLayoutPanel',
15803             // technicall
15804         }
15805      ]
15806  *})
15807  *
15808  *
15809  * It can be used to build a big heiracy, with parent etc.
15810  * or you can just use this to render a single compoent to a dom element
15811  * MYPART.render(Roo.Element | String(id) | dom_element )
15812  *
15813  *
15814  * Usage patterns.
15815  *
15816  * Classic Roo
15817  *
15818  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15819  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15820  *
15821  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15822  *
15823  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15824  * - if mulitple topModules exist, the last one is defined as the top module.
15825  *
15826  * Embeded Roo
15827  * 
15828  * When the top level or multiple modules are to embedded into a existing HTML page,
15829  * the parent element can container '#id' of the element where the module will be drawn.
15830  *
15831  * Bootstrap Roo
15832  *
15833  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15834  * it relies more on a include mechanism, where sub modules are included into an outer page.
15835  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15836  * 
15837  * Bootstrap Roo Included elements
15838  *
15839  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15840  * hence confusing the component builder as it thinks there are multiple top level elements. 
15841  *
15842  * 
15843  * 
15844  * @extends Roo.util.Observable
15845  * @constructor
15846  * @param cfg {Object} configuration of component
15847  * 
15848  */
15849 Roo.XComponent = function(cfg) {
15850     Roo.apply(this, cfg);
15851     this.addEvents({ 
15852         /**
15853              * @event built
15854              * Fires when this the componnt is built
15855              * @param {Roo.XComponent} c the component
15856              */
15857         'built' : true
15858         
15859     });
15860     this.region = this.region || 'center'; // default..
15861     Roo.XComponent.register(this);
15862     this.modules = false;
15863     this.el = false; // where the layout goes..
15864     
15865     
15866 }
15867 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15868     /**
15869      * @property el
15870      * The created element (with Roo.factory())
15871      * @type {Roo.Layout}
15872      */
15873     el  : false,
15874     
15875     /**
15876      * @property el
15877      * for BC  - use el in new code
15878      * @type {Roo.Layout}
15879      */
15880     panel : false,
15881     
15882     /**
15883      * @property layout
15884      * for BC  - use el in new code
15885      * @type {Roo.Layout}
15886      */
15887     layout : false,
15888     
15889      /**
15890      * @cfg {Function|boolean} disabled
15891      * If this module is disabled by some rule, return true from the funtion
15892      */
15893     disabled : false,
15894     
15895     /**
15896      * @cfg {String} parent 
15897      * Name of parent element which it get xtype added to..
15898      */
15899     parent: false,
15900     
15901     /**
15902      * @cfg {String} order
15903      * Used to set the order in which elements are created (usefull for multiple tabs)
15904      */
15905     
15906     order : false,
15907     /**
15908      * @cfg {String} name
15909      * String to display while loading.
15910      */
15911     name : false,
15912     /**
15913      * @cfg {String} region
15914      * Region to render component to (defaults to center)
15915      */
15916     region : 'center',
15917     
15918     /**
15919      * @cfg {Array} items
15920      * A single item array - the first element is the root of the tree..
15921      * It's done this way to stay compatible with the Xtype system...
15922      */
15923     items : false,
15924     
15925     /**
15926      * @property _tree
15927      * The method that retuns the tree of parts that make up this compoennt 
15928      * @type {function}
15929      */
15930     _tree  : false,
15931     
15932      /**
15933      * render
15934      * render element to dom or tree
15935      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15936      */
15937     
15938     render : function(el)
15939     {
15940         
15941         el = el || false;
15942         var hp = this.parent ? 1 : 0;
15943         Roo.debug &&  Roo.log(this);
15944         
15945         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15946             // if parent is a '#.....' string, then let's use that..
15947             var ename = this.parent.substr(1);
15948             this.parent = false;
15949             Roo.debug && Roo.log(ename);
15950             switch (ename) {
15951                 case 'bootstrap-body' :
15952                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15953                         this.parent = { el :  new  Roo.bootstrap.Body() };
15954                         Roo.debug && Roo.log("setting el to doc body");
15955                          
15956                     } else {
15957                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15958                     }
15959                     break;
15960                 case 'bootstrap':
15961                     this.parent = { el : true};
15962                     // fall through
15963                 default:
15964                     el = Roo.get(ename);
15965                     break;
15966             }
15967                 
15968             
15969             if (!el && !this.parent) {
15970                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
15971                 return;
15972             }
15973         }
15974         Roo.debug && Roo.log("EL:");
15975         Roo.debug && Roo.log(el);
15976         Roo.debug && Roo.log("this.parent.el:");
15977         Roo.debug && Roo.log(this.parent.el);
15978         
15979         var tree = this._tree ? this._tree() : this.tree();
15980
15981         // altertive root elements ??? - we need a better way to indicate these.
15982         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
15983                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
15984         
15985         if (!this.parent && is_alt) {
15986             //el = Roo.get(document.body);
15987             this.parent = { el : true };
15988         }
15989             
15990             
15991         
15992         if (!this.parent) {
15993             
15994             Roo.debug && Roo.log("no parent - creating one");
15995             
15996             el = el ? Roo.get(el) : false;      
15997             
15998             // it's a top level one..
15999             this.parent =  {
16000                 el : new Roo.BorderLayout(el || document.body, {
16001                 
16002                      center: {
16003                          titlebar: false,
16004                          autoScroll:false,
16005                          closeOnTab: true,
16006                          tabPosition: 'top',
16007                           //resizeTabs: true,
16008                          alwaysShowTabs: el && hp? false :  true,
16009                          hideTabs: el || !hp ? true :  false,
16010                          minTabWidth: 140
16011                      }
16012                  })
16013             }
16014         }
16015         
16016         if (!this.parent.el) {
16017                 // probably an old style ctor, which has been disabled.
16018                 return;
16019
16020         }
16021                 // The 'tree' method is  '_tree now' 
16022             
16023         tree.region = tree.region || this.region;
16024         
16025         if (this.parent.el === true) {
16026             // bootstrap... - body..
16027             this.parent.el = Roo.factory(tree);
16028         }
16029         
16030         this.el = this.parent.el.addxtype(tree);
16031         this.fireEvent('built', this);
16032         
16033         this.panel = this.el;
16034         this.layout = this.panel.layout;
16035         this.parentLayout = this.parent.layout  || false;  
16036          
16037     }
16038     
16039 });
16040
16041 Roo.apply(Roo.XComponent, {
16042     /**
16043      * @property  hideProgress
16044      * true to disable the building progress bar.. usefull on single page renders.
16045      * @type Boolean
16046      */
16047     hideProgress : false,
16048     /**
16049      * @property  buildCompleted
16050      * True when the builder has completed building the interface.
16051      * @type Boolean
16052      */
16053     buildCompleted : false,
16054      
16055     /**
16056      * @property  topModule
16057      * the upper most module - uses document.element as it's constructor.
16058      * @type Object
16059      */
16060      
16061     topModule  : false,
16062       
16063     /**
16064      * @property  modules
16065      * array of modules to be created by registration system.
16066      * @type {Array} of Roo.XComponent
16067      */
16068     
16069     modules : [],
16070     /**
16071      * @property  elmodules
16072      * array of modules to be created by which use #ID 
16073      * @type {Array} of Roo.XComponent
16074      */
16075      
16076     elmodules : [],
16077
16078      /**
16079      * @property  build_from_html
16080      * Build elements from html - used by bootstrap HTML stuff 
16081      *    - this is cleared after build is completed
16082      * @type {boolean} true  (default false)
16083      */
16084      
16085     build_from_html : false,
16086
16087     /**
16088      * Register components to be built later.
16089      *
16090      * This solves the following issues
16091      * - Building is not done on page load, but after an authentication process has occured.
16092      * - Interface elements are registered on page load
16093      * - Parent Interface elements may not be loaded before child, so this handles that..
16094      * 
16095      *
16096      * example:
16097      * 
16098      * MyApp.register({
16099           order : '000001',
16100           module : 'Pman.Tab.projectMgr',
16101           region : 'center',
16102           parent : 'Pman.layout',
16103           disabled : false,  // or use a function..
16104         })
16105      
16106      * * @param {Object} details about module
16107      */
16108     register : function(obj) {
16109                 
16110         Roo.XComponent.event.fireEvent('register', obj);
16111         switch(typeof(obj.disabled) ) {
16112                 
16113             case 'undefined':
16114                 break;
16115             
16116             case 'function':
16117                 if ( obj.disabled() ) {
16118                         return;
16119                 }
16120                 break;
16121             
16122             default:
16123                 if (obj.disabled) {
16124                         return;
16125                 }
16126                 break;
16127         }
16128                 
16129         this.modules.push(obj);
16130          
16131     },
16132     /**
16133      * convert a string to an object..
16134      * eg. 'AAA.BBB' -> finds AAA.BBB
16135
16136      */
16137     
16138     toObject : function(str)
16139     {
16140         if (!str || typeof(str) == 'object') {
16141             return str;
16142         }
16143         if (str.substring(0,1) == '#') {
16144             return str;
16145         }
16146
16147         var ar = str.split('.');
16148         var rt, o;
16149         rt = ar.shift();
16150             /** eval:var:o */
16151         try {
16152             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16153         } catch (e) {
16154             throw "Module not found : " + str;
16155         }
16156         
16157         if (o === false) {
16158             throw "Module not found : " + str;
16159         }
16160         Roo.each(ar, function(e) {
16161             if (typeof(o[e]) == 'undefined') {
16162                 throw "Module not found : " + str;
16163             }
16164             o = o[e];
16165         });
16166         
16167         return o;
16168         
16169     },
16170     
16171     
16172     /**
16173      * move modules into their correct place in the tree..
16174      * 
16175      */
16176     preBuild : function ()
16177     {
16178         var _t = this;
16179         Roo.each(this.modules , function (obj)
16180         {
16181             Roo.XComponent.event.fireEvent('beforebuild', obj);
16182             
16183             var opar = obj.parent;
16184             try { 
16185                 obj.parent = this.toObject(opar);
16186             } catch(e) {
16187                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16188                 return;
16189             }
16190             
16191             if (!obj.parent) {
16192                 Roo.debug && Roo.log("GOT top level module");
16193                 Roo.debug && Roo.log(obj);
16194                 obj.modules = new Roo.util.MixedCollection(false, 
16195                     function(o) { return o.order + '' }
16196                 );
16197                 this.topModule = obj;
16198                 return;
16199             }
16200                         // parent is a string (usually a dom element name..)
16201             if (typeof(obj.parent) == 'string') {
16202                 this.elmodules.push(obj);
16203                 return;
16204             }
16205             if (obj.parent.constructor != Roo.XComponent) {
16206                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16207             }
16208             if (!obj.parent.modules) {
16209                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16210                     function(o) { return o.order + '' }
16211                 );
16212             }
16213             if (obj.parent.disabled) {
16214                 obj.disabled = true;
16215             }
16216             obj.parent.modules.add(obj);
16217         }, this);
16218     },
16219     
16220      /**
16221      * make a list of modules to build.
16222      * @return {Array} list of modules. 
16223      */ 
16224     
16225     buildOrder : function()
16226     {
16227         var _this = this;
16228         var cmp = function(a,b) {   
16229             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16230         };
16231         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16232             throw "No top level modules to build";
16233         }
16234         
16235         // make a flat list in order of modules to build.
16236         var mods = this.topModule ? [ this.topModule ] : [];
16237                 
16238         
16239         // elmodules (is a list of DOM based modules )
16240         Roo.each(this.elmodules, function(e) {
16241             mods.push(e);
16242             if (!this.topModule &&
16243                 typeof(e.parent) == 'string' &&
16244                 e.parent.substring(0,1) == '#' &&
16245                 Roo.get(e.parent.substr(1))
16246                ) {
16247                 
16248                 _this.topModule = e;
16249             }
16250             
16251         });
16252
16253         
16254         // add modules to their parents..
16255         var addMod = function(m) {
16256             Roo.debug && Roo.log("build Order: add: " + m.name);
16257                 
16258             mods.push(m);
16259             if (m.modules && !m.disabled) {
16260                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16261                 m.modules.keySort('ASC',  cmp );
16262                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16263     
16264                 m.modules.each(addMod);
16265             } else {
16266                 Roo.debug && Roo.log("build Order: no child modules");
16267             }
16268             // not sure if this is used any more..
16269             if (m.finalize) {
16270                 m.finalize.name = m.name + " (clean up) ";
16271                 mods.push(m.finalize);
16272             }
16273             
16274         }
16275         if (this.topModule && this.topModule.modules) { 
16276             this.topModule.modules.keySort('ASC',  cmp );
16277             this.topModule.modules.each(addMod);
16278         } 
16279         return mods;
16280     },
16281     
16282      /**
16283      * Build the registered modules.
16284      * @param {Object} parent element.
16285      * @param {Function} optional method to call after module has been added.
16286      * 
16287      */ 
16288    
16289     build : function(opts) 
16290     {
16291         
16292         if (typeof(opts) != 'undefined') {
16293             Roo.apply(this,opts);
16294         }
16295         
16296         this.preBuild();
16297         var mods = this.buildOrder();
16298       
16299         //this.allmods = mods;
16300         //Roo.debug && Roo.log(mods);
16301         //return;
16302         if (!mods.length) { // should not happen
16303             throw "NO modules!!!";
16304         }
16305         
16306         
16307         var msg = "Building Interface...";
16308         // flash it up as modal - so we store the mask!?
16309         if (!this.hideProgress && Roo.MessageBox) {
16310             Roo.MessageBox.show({ title: 'loading' });
16311             Roo.MessageBox.show({
16312                title: "Please wait...",
16313                msg: msg,
16314                width:450,
16315                progress:true,
16316                closable:false,
16317                modal: false
16318               
16319             });
16320         }
16321         var total = mods.length;
16322         
16323         var _this = this;
16324         var progressRun = function() {
16325             if (!mods.length) {
16326                 Roo.debug && Roo.log('hide?');
16327                 if (!this.hideProgress && Roo.MessageBox) {
16328                     Roo.MessageBox.hide();
16329                 }
16330                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16331                 
16332                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16333                 
16334                 // THE END...
16335                 return false;   
16336             }
16337             
16338             var m = mods.shift();
16339             
16340             
16341             Roo.debug && Roo.log(m);
16342             // not sure if this is supported any more.. - modules that are are just function
16343             if (typeof(m) == 'function') { 
16344                 m.call(this);
16345                 return progressRun.defer(10, _this);
16346             } 
16347             
16348             
16349             msg = "Building Interface " + (total  - mods.length) + 
16350                     " of " + total + 
16351                     (m.name ? (' - ' + m.name) : '');
16352                         Roo.debug && Roo.log(msg);
16353             if (!this.hideProgress &&  Roo.MessageBox) { 
16354                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16355             }
16356             
16357          
16358             // is the module disabled?
16359             var disabled = (typeof(m.disabled) == 'function') ?
16360                 m.disabled.call(m.module.disabled) : m.disabled;    
16361             
16362             
16363             if (disabled) {
16364                 return progressRun(); // we do not update the display!
16365             }
16366             
16367             // now build 
16368             
16369                         
16370                         
16371             m.render();
16372             // it's 10 on top level, and 1 on others??? why...
16373             return progressRun.defer(10, _this);
16374              
16375         }
16376         progressRun.defer(1, _this);
16377      
16378         
16379         
16380     },
16381         
16382         
16383         /**
16384          * Event Object.
16385          *
16386          *
16387          */
16388         event: false, 
16389     /**
16390          * wrapper for event.on - aliased later..  
16391          * Typically use to register a event handler for register:
16392          *
16393          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16394          *
16395          */
16396     on : false
16397    
16398     
16399     
16400 });
16401
16402 Roo.XComponent.event = new Roo.util.Observable({
16403                 events : { 
16404                         /**
16405                          * @event register
16406                          * Fires when an Component is registered,
16407                          * set the disable property on the Component to stop registration.
16408                          * @param {Roo.XComponent} c the component being registerd.
16409                          * 
16410                          */
16411                         'register' : true,
16412             /**
16413                          * @event beforebuild
16414                          * Fires before each Component is built
16415                          * can be used to apply permissions.
16416                          * @param {Roo.XComponent} c the component being registerd.
16417                          * 
16418                          */
16419                         'beforebuild' : true,
16420                         /**
16421                          * @event buildcomplete
16422                          * Fires on the top level element when all elements have been built
16423                          * @param {Roo.XComponent} the top level component.
16424                          */
16425                         'buildcomplete' : true
16426                         
16427                 }
16428 });
16429
16430 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16431  /*
16432  * Based on:
16433  * Ext JS Library 1.1.1
16434  * Copyright(c) 2006-2007, Ext JS, LLC.
16435  *
16436  * Originally Released Under LGPL - original licence link has changed is not relivant.
16437  *
16438  * Fork - LGPL
16439  * <script type="text/javascript">
16440  */
16441
16442
16443
16444 /*
16445  * These classes are derivatives of the similarly named classes in the YUI Library.
16446  * The original license:
16447  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16448  * Code licensed under the BSD License:
16449  * http://developer.yahoo.net/yui/license.txt
16450  */
16451
16452 (function() {
16453
16454 var Event=Roo.EventManager;
16455 var Dom=Roo.lib.Dom;
16456
16457 /**
16458  * @class Roo.dd.DragDrop
16459  * @extends Roo.util.Observable
16460  * Defines the interface and base operation of items that that can be
16461  * dragged or can be drop targets.  It was designed to be extended, overriding
16462  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16463  * Up to three html elements can be associated with a DragDrop instance:
16464  * <ul>
16465  * <li>linked element: the element that is passed into the constructor.
16466  * This is the element which defines the boundaries for interaction with
16467  * other DragDrop objects.</li>
16468  * <li>handle element(s): The drag operation only occurs if the element that
16469  * was clicked matches a handle element.  By default this is the linked
16470  * element, but there are times that you will want only a portion of the
16471  * linked element to initiate the drag operation, and the setHandleElId()
16472  * method provides a way to define this.</li>
16473  * <li>drag element: this represents the element that would be moved along
16474  * with the cursor during a drag operation.  By default, this is the linked
16475  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16476  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16477  * </li>
16478  * </ul>
16479  * This class should not be instantiated until the onload event to ensure that
16480  * the associated elements are available.
16481  * The following would define a DragDrop obj that would interact with any
16482  * other DragDrop obj in the "group1" group:
16483  * <pre>
16484  *  dd = new Roo.dd.DragDrop("div1", "group1");
16485  * </pre>
16486  * Since none of the event handlers have been implemented, nothing would
16487  * actually happen if you were to run the code above.  Normally you would
16488  * override this class or one of the default implementations, but you can
16489  * also override the methods you want on an instance of the class...
16490  * <pre>
16491  *  dd.onDragDrop = function(e, id) {
16492  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16493  *  }
16494  * </pre>
16495  * @constructor
16496  * @param {String} id of the element that is linked to this instance
16497  * @param {String} sGroup the group of related DragDrop objects
16498  * @param {object} config an object containing configurable attributes
16499  *                Valid properties for DragDrop:
16500  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16501  */
16502 Roo.dd.DragDrop = function(id, sGroup, config) {
16503     if (id) {
16504         this.init(id, sGroup, config);
16505     }
16506     
16507 };
16508
16509 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16510
16511     /**
16512      * The id of the element associated with this object.  This is what we
16513      * refer to as the "linked element" because the size and position of
16514      * this element is used to determine when the drag and drop objects have
16515      * interacted.
16516      * @property id
16517      * @type String
16518      */
16519     id: null,
16520
16521     /**
16522      * Configuration attributes passed into the constructor
16523      * @property config
16524      * @type object
16525      */
16526     config: null,
16527
16528     /**
16529      * The id of the element that will be dragged.  By default this is same
16530      * as the linked element , but could be changed to another element. Ex:
16531      * Roo.dd.DDProxy
16532      * @property dragElId
16533      * @type String
16534      * @private
16535      */
16536     dragElId: null,
16537
16538     /**
16539      * the id of the element that initiates the drag operation.  By default
16540      * this is the linked element, but could be changed to be a child of this
16541      * element.  This lets us do things like only starting the drag when the
16542      * header element within the linked html element is clicked.
16543      * @property handleElId
16544      * @type String
16545      * @private
16546      */
16547     handleElId: null,
16548
16549     /**
16550      * An associative array of HTML tags that will be ignored if clicked.
16551      * @property invalidHandleTypes
16552      * @type {string: string}
16553      */
16554     invalidHandleTypes: null,
16555
16556     /**
16557      * An associative array of ids for elements that will be ignored if clicked
16558      * @property invalidHandleIds
16559      * @type {string: string}
16560      */
16561     invalidHandleIds: null,
16562
16563     /**
16564      * An indexted array of css class names for elements that will be ignored
16565      * if clicked.
16566      * @property invalidHandleClasses
16567      * @type string[]
16568      */
16569     invalidHandleClasses: null,
16570
16571     /**
16572      * The linked element's absolute X position at the time the drag was
16573      * started
16574      * @property startPageX
16575      * @type int
16576      * @private
16577      */
16578     startPageX: 0,
16579
16580     /**
16581      * The linked element's absolute X position at the time the drag was
16582      * started
16583      * @property startPageY
16584      * @type int
16585      * @private
16586      */
16587     startPageY: 0,
16588
16589     /**
16590      * The group defines a logical collection of DragDrop objects that are
16591      * related.  Instances only get events when interacting with other
16592      * DragDrop object in the same group.  This lets us define multiple
16593      * groups using a single DragDrop subclass if we want.
16594      * @property groups
16595      * @type {string: string}
16596      */
16597     groups: null,
16598
16599     /**
16600      * Individual drag/drop instances can be locked.  This will prevent
16601      * onmousedown start drag.
16602      * @property locked
16603      * @type boolean
16604      * @private
16605      */
16606     locked: false,
16607
16608     /**
16609      * Lock this instance
16610      * @method lock
16611      */
16612     lock: function() { this.locked = true; },
16613
16614     /**
16615      * Unlock this instace
16616      * @method unlock
16617      */
16618     unlock: function() { this.locked = false; },
16619
16620     /**
16621      * By default, all insances can be a drop target.  This can be disabled by
16622      * setting isTarget to false.
16623      * @method isTarget
16624      * @type boolean
16625      */
16626     isTarget: true,
16627
16628     /**
16629      * The padding configured for this drag and drop object for calculating
16630      * the drop zone intersection with this object.
16631      * @method padding
16632      * @type int[]
16633      */
16634     padding: null,
16635
16636     /**
16637      * Cached reference to the linked element
16638      * @property _domRef
16639      * @private
16640      */
16641     _domRef: null,
16642
16643     /**
16644      * Internal typeof flag
16645      * @property __ygDragDrop
16646      * @private
16647      */
16648     __ygDragDrop: true,
16649
16650     /**
16651      * Set to true when horizontal contraints are applied
16652      * @property constrainX
16653      * @type boolean
16654      * @private
16655      */
16656     constrainX: false,
16657
16658     /**
16659      * Set to true when vertical contraints are applied
16660      * @property constrainY
16661      * @type boolean
16662      * @private
16663      */
16664     constrainY: false,
16665
16666     /**
16667      * The left constraint
16668      * @property minX
16669      * @type int
16670      * @private
16671      */
16672     minX: 0,
16673
16674     /**
16675      * The right constraint
16676      * @property maxX
16677      * @type int
16678      * @private
16679      */
16680     maxX: 0,
16681
16682     /**
16683      * The up constraint
16684      * @property minY
16685      * @type int
16686      * @type int
16687      * @private
16688      */
16689     minY: 0,
16690
16691     /**
16692      * The down constraint
16693      * @property maxY
16694      * @type int
16695      * @private
16696      */
16697     maxY: 0,
16698
16699     /**
16700      * Maintain offsets when we resetconstraints.  Set to true when you want
16701      * the position of the element relative to its parent to stay the same
16702      * when the page changes
16703      *
16704      * @property maintainOffset
16705      * @type boolean
16706      */
16707     maintainOffset: false,
16708
16709     /**
16710      * Array of pixel locations the element will snap to if we specified a
16711      * horizontal graduation/interval.  This array is generated automatically
16712      * when you define a tick interval.
16713      * @property xTicks
16714      * @type int[]
16715      */
16716     xTicks: null,
16717
16718     /**
16719      * Array of pixel locations the element will snap to if we specified a
16720      * vertical graduation/interval.  This array is generated automatically
16721      * when you define a tick interval.
16722      * @property yTicks
16723      * @type int[]
16724      */
16725     yTicks: null,
16726
16727     /**
16728      * By default the drag and drop instance will only respond to the primary
16729      * button click (left button for a right-handed mouse).  Set to true to
16730      * allow drag and drop to start with any mouse click that is propogated
16731      * by the browser
16732      * @property primaryButtonOnly
16733      * @type boolean
16734      */
16735     primaryButtonOnly: true,
16736
16737     /**
16738      * The availabe property is false until the linked dom element is accessible.
16739      * @property available
16740      * @type boolean
16741      */
16742     available: false,
16743
16744     /**
16745      * By default, drags can only be initiated if the mousedown occurs in the
16746      * region the linked element is.  This is done in part to work around a
16747      * bug in some browsers that mis-report the mousedown if the previous
16748      * mouseup happened outside of the window.  This property is set to true
16749      * if outer handles are defined.
16750      *
16751      * @property hasOuterHandles
16752      * @type boolean
16753      * @default false
16754      */
16755     hasOuterHandles: false,
16756
16757     /**
16758      * Code that executes immediately before the startDrag event
16759      * @method b4StartDrag
16760      * @private
16761      */
16762     b4StartDrag: function(x, y) { },
16763
16764     /**
16765      * Abstract method called after a drag/drop object is clicked
16766      * and the drag or mousedown time thresholds have beeen met.
16767      * @method startDrag
16768      * @param {int} X click location
16769      * @param {int} Y click location
16770      */
16771     startDrag: function(x, y) { /* override this */ },
16772
16773     /**
16774      * Code that executes immediately before the onDrag event
16775      * @method b4Drag
16776      * @private
16777      */
16778     b4Drag: function(e) { },
16779
16780     /**
16781      * Abstract method called during the onMouseMove event while dragging an
16782      * object.
16783      * @method onDrag
16784      * @param {Event} e the mousemove event
16785      */
16786     onDrag: function(e) { /* override this */ },
16787
16788     /**
16789      * Abstract method called when this element fist begins hovering over
16790      * another DragDrop obj
16791      * @method onDragEnter
16792      * @param {Event} e the mousemove event
16793      * @param {String|DragDrop[]} id In POINT mode, the element
16794      * id this is hovering over.  In INTERSECT mode, an array of one or more
16795      * dragdrop items being hovered over.
16796      */
16797     onDragEnter: function(e, id) { /* override this */ },
16798
16799     /**
16800      * Code that executes immediately before the onDragOver event
16801      * @method b4DragOver
16802      * @private
16803      */
16804     b4DragOver: function(e) { },
16805
16806     /**
16807      * Abstract method called when this element is hovering over another
16808      * DragDrop obj
16809      * @method onDragOver
16810      * @param {Event} e the mousemove event
16811      * @param {String|DragDrop[]} id In POINT mode, the element
16812      * id this is hovering over.  In INTERSECT mode, an array of dd items
16813      * being hovered over.
16814      */
16815     onDragOver: function(e, id) { /* override this */ },
16816
16817     /**
16818      * Code that executes immediately before the onDragOut event
16819      * @method b4DragOut
16820      * @private
16821      */
16822     b4DragOut: function(e) { },
16823
16824     /**
16825      * Abstract method called when we are no longer hovering over an element
16826      * @method onDragOut
16827      * @param {Event} e the mousemove event
16828      * @param {String|DragDrop[]} id In POINT mode, the element
16829      * id this was hovering over.  In INTERSECT mode, an array of dd items
16830      * that the mouse is no longer over.
16831      */
16832     onDragOut: function(e, id) { /* override this */ },
16833
16834     /**
16835      * Code that executes immediately before the onDragDrop event
16836      * @method b4DragDrop
16837      * @private
16838      */
16839     b4DragDrop: function(e) { },
16840
16841     /**
16842      * Abstract method called when this item is dropped on another DragDrop
16843      * obj
16844      * @method onDragDrop
16845      * @param {Event} e the mouseup event
16846      * @param {String|DragDrop[]} id In POINT mode, the element
16847      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16848      * was dropped on.
16849      */
16850     onDragDrop: function(e, id) { /* override this */ },
16851
16852     /**
16853      * Abstract method called when this item is dropped on an area with no
16854      * drop target
16855      * @method onInvalidDrop
16856      * @param {Event} e the mouseup event
16857      */
16858     onInvalidDrop: function(e) { /* override this */ },
16859
16860     /**
16861      * Code that executes immediately before the endDrag event
16862      * @method b4EndDrag
16863      * @private
16864      */
16865     b4EndDrag: function(e) { },
16866
16867     /**
16868      * Fired when we are done dragging the object
16869      * @method endDrag
16870      * @param {Event} e the mouseup event
16871      */
16872     endDrag: function(e) { /* override this */ },
16873
16874     /**
16875      * Code executed immediately before the onMouseDown event
16876      * @method b4MouseDown
16877      * @param {Event} e the mousedown event
16878      * @private
16879      */
16880     b4MouseDown: function(e) {  },
16881
16882     /**
16883      * Event handler that fires when a drag/drop obj gets a mousedown
16884      * @method onMouseDown
16885      * @param {Event} e the mousedown event
16886      */
16887     onMouseDown: function(e) { /* override this */ },
16888
16889     /**
16890      * Event handler that fires when a drag/drop obj gets a mouseup
16891      * @method onMouseUp
16892      * @param {Event} e the mouseup event
16893      */
16894     onMouseUp: function(e) { /* override this */ },
16895
16896     /**
16897      * Override the onAvailable method to do what is needed after the initial
16898      * position was determined.
16899      * @method onAvailable
16900      */
16901     onAvailable: function () {
16902     },
16903
16904     /*
16905      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16906      * @type Object
16907      */
16908     defaultPadding : {left:0, right:0, top:0, bottom:0},
16909
16910     /*
16911      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16912  *
16913  * Usage:
16914  <pre><code>
16915  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16916                 { dragElId: "existingProxyDiv" });
16917  dd.startDrag = function(){
16918      this.constrainTo("parent-id");
16919  };
16920  </code></pre>
16921  * Or you can initalize it using the {@link Roo.Element} object:
16922  <pre><code>
16923  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16924      startDrag : function(){
16925          this.constrainTo("parent-id");
16926      }
16927  });
16928  </code></pre>
16929      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16930      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16931      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16932      * an object containing the sides to pad. For example: {right:10, bottom:10}
16933      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16934      */
16935     constrainTo : function(constrainTo, pad, inContent){
16936         if(typeof pad == "number"){
16937             pad = {left: pad, right:pad, top:pad, bottom:pad};
16938         }
16939         pad = pad || this.defaultPadding;
16940         var b = Roo.get(this.getEl()).getBox();
16941         var ce = Roo.get(constrainTo);
16942         var s = ce.getScroll();
16943         var c, cd = ce.dom;
16944         if(cd == document.body){
16945             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16946         }else{
16947             xy = ce.getXY();
16948             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16949         }
16950
16951
16952         var topSpace = b.y - c.y;
16953         var leftSpace = b.x - c.x;
16954
16955         this.resetConstraints();
16956         this.setXConstraint(leftSpace - (pad.left||0), // left
16957                 c.width - leftSpace - b.width - (pad.right||0) //right
16958         );
16959         this.setYConstraint(topSpace - (pad.top||0), //top
16960                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16961         );
16962     },
16963
16964     /**
16965      * Returns a reference to the linked element
16966      * @method getEl
16967      * @return {HTMLElement} the html element
16968      */
16969     getEl: function() {
16970         if (!this._domRef) {
16971             this._domRef = Roo.getDom(this.id);
16972         }
16973
16974         return this._domRef;
16975     },
16976
16977     /**
16978      * Returns a reference to the actual element to drag.  By default this is
16979      * the same as the html element, but it can be assigned to another
16980      * element. An example of this can be found in Roo.dd.DDProxy
16981      * @method getDragEl
16982      * @return {HTMLElement} the html element
16983      */
16984     getDragEl: function() {
16985         return Roo.getDom(this.dragElId);
16986     },
16987
16988     /**
16989      * Sets up the DragDrop object.  Must be called in the constructor of any
16990      * Roo.dd.DragDrop subclass
16991      * @method init
16992      * @param id the id of the linked element
16993      * @param {String} sGroup the group of related items
16994      * @param {object} config configuration attributes
16995      */
16996     init: function(id, sGroup, config) {
16997         this.initTarget(id, sGroup, config);
16998         if (!Roo.isTouch) {
16999             Event.on(this.id, "mousedown", this.handleMouseDown, this);
17000         }
17001         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17002         // Event.on(this.id, "selectstart", Event.preventDefault);
17003     },
17004
17005     /**
17006      * Initializes Targeting functionality only... the object does not
17007      * get a mousedown handler.
17008      * @method initTarget
17009      * @param id the id of the linked element
17010      * @param {String} sGroup the group of related items
17011      * @param {object} config configuration attributes
17012      */
17013     initTarget: function(id, sGroup, config) {
17014
17015         // configuration attributes
17016         this.config = config || {};
17017
17018         // create a local reference to the drag and drop manager
17019         this.DDM = Roo.dd.DDM;
17020         // initialize the groups array
17021         this.groups = {};
17022
17023         // assume that we have an element reference instead of an id if the
17024         // parameter is not a string
17025         if (typeof id !== "string") {
17026             id = Roo.id(id);
17027         }
17028
17029         // set the id
17030         this.id = id;
17031
17032         // add to an interaction group
17033         this.addToGroup((sGroup) ? sGroup : "default");
17034
17035         // We don't want to register this as the handle with the manager
17036         // so we just set the id rather than calling the setter.
17037         this.handleElId = id;
17038
17039         // the linked element is the element that gets dragged by default
17040         this.setDragElId(id);
17041
17042         // by default, clicked anchors will not start drag operations.
17043         this.invalidHandleTypes = { A: "A" };
17044         this.invalidHandleIds = {};
17045         this.invalidHandleClasses = [];
17046
17047         this.applyConfig();
17048
17049         this.handleOnAvailable();
17050     },
17051
17052     /**
17053      * Applies the configuration parameters that were passed into the constructor.
17054      * This is supposed to happen at each level through the inheritance chain.  So
17055      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17056      * DragDrop in order to get all of the parameters that are available in
17057      * each object.
17058      * @method applyConfig
17059      */
17060     applyConfig: function() {
17061
17062         // configurable properties:
17063         //    padding, isTarget, maintainOffset, primaryButtonOnly
17064         this.padding           = this.config.padding || [0, 0, 0, 0];
17065         this.isTarget          = (this.config.isTarget !== false);
17066         this.maintainOffset    = (this.config.maintainOffset);
17067         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17068
17069     },
17070
17071     /**
17072      * Executed when the linked element is available
17073      * @method handleOnAvailable
17074      * @private
17075      */
17076     handleOnAvailable: function() {
17077         this.available = true;
17078         this.resetConstraints();
17079         this.onAvailable();
17080     },
17081
17082      /**
17083      * Configures the padding for the target zone in px.  Effectively expands
17084      * (or reduces) the virtual object size for targeting calculations.
17085      * Supports css-style shorthand; if only one parameter is passed, all sides
17086      * will have that padding, and if only two are passed, the top and bottom
17087      * will have the first param, the left and right the second.
17088      * @method setPadding
17089      * @param {int} iTop    Top pad
17090      * @param {int} iRight  Right pad
17091      * @param {int} iBot    Bot pad
17092      * @param {int} iLeft   Left pad
17093      */
17094     setPadding: function(iTop, iRight, iBot, iLeft) {
17095         // this.padding = [iLeft, iRight, iTop, iBot];
17096         if (!iRight && 0 !== iRight) {
17097             this.padding = [iTop, iTop, iTop, iTop];
17098         } else if (!iBot && 0 !== iBot) {
17099             this.padding = [iTop, iRight, iTop, iRight];
17100         } else {
17101             this.padding = [iTop, iRight, iBot, iLeft];
17102         }
17103     },
17104
17105     /**
17106      * Stores the initial placement of the linked element.
17107      * @method setInitialPosition
17108      * @param {int} diffX   the X offset, default 0
17109      * @param {int} diffY   the Y offset, default 0
17110      */
17111     setInitPosition: function(diffX, diffY) {
17112         var el = this.getEl();
17113
17114         if (!this.DDM.verifyEl(el)) {
17115             return;
17116         }
17117
17118         var dx = diffX || 0;
17119         var dy = diffY || 0;
17120
17121         var p = Dom.getXY( el );
17122
17123         this.initPageX = p[0] - dx;
17124         this.initPageY = p[1] - dy;
17125
17126         this.lastPageX = p[0];
17127         this.lastPageY = p[1];
17128
17129
17130         this.setStartPosition(p);
17131     },
17132
17133     /**
17134      * Sets the start position of the element.  This is set when the obj
17135      * is initialized, the reset when a drag is started.
17136      * @method setStartPosition
17137      * @param pos current position (from previous lookup)
17138      * @private
17139      */
17140     setStartPosition: function(pos) {
17141         var p = pos || Dom.getXY( this.getEl() );
17142         this.deltaSetXY = null;
17143
17144         this.startPageX = p[0];
17145         this.startPageY = p[1];
17146     },
17147
17148     /**
17149      * Add this instance to a group of related drag/drop objects.  All
17150      * instances belong to at least one group, and can belong to as many
17151      * groups as needed.
17152      * @method addToGroup
17153      * @param sGroup {string} the name of the group
17154      */
17155     addToGroup: function(sGroup) {
17156         this.groups[sGroup] = true;
17157         this.DDM.regDragDrop(this, sGroup);
17158     },
17159
17160     /**
17161      * Remove's this instance from the supplied interaction group
17162      * @method removeFromGroup
17163      * @param {string}  sGroup  The group to drop
17164      */
17165     removeFromGroup: function(sGroup) {
17166         if (this.groups[sGroup]) {
17167             delete this.groups[sGroup];
17168         }
17169
17170         this.DDM.removeDDFromGroup(this, sGroup);
17171     },
17172
17173     /**
17174      * Allows you to specify that an element other than the linked element
17175      * will be moved with the cursor during a drag
17176      * @method setDragElId
17177      * @param id {string} the id of the element that will be used to initiate the drag
17178      */
17179     setDragElId: function(id) {
17180         this.dragElId = id;
17181     },
17182
17183     /**
17184      * Allows you to specify a child of the linked element that should be
17185      * used to initiate the drag operation.  An example of this would be if
17186      * you have a content div with text and links.  Clicking anywhere in the
17187      * content area would normally start the drag operation.  Use this method
17188      * to specify that an element inside of the content div is the element
17189      * that starts the drag operation.
17190      * @method setHandleElId
17191      * @param id {string} the id of the element that will be used to
17192      * initiate the drag.
17193      */
17194     setHandleElId: function(id) {
17195         if (typeof id !== "string") {
17196             id = Roo.id(id);
17197         }
17198         this.handleElId = id;
17199         this.DDM.regHandle(this.id, id);
17200     },
17201
17202     /**
17203      * Allows you to set an element outside of the linked element as a drag
17204      * handle
17205      * @method setOuterHandleElId
17206      * @param id the id of the element that will be used to initiate the drag
17207      */
17208     setOuterHandleElId: function(id) {
17209         if (typeof id !== "string") {
17210             id = Roo.id(id);
17211         }
17212         Event.on(id, "mousedown",
17213                 this.handleMouseDown, this);
17214         this.setHandleElId(id);
17215
17216         this.hasOuterHandles = true;
17217     },
17218
17219     /**
17220      * Remove all drag and drop hooks for this element
17221      * @method unreg
17222      */
17223     unreg: function() {
17224         Event.un(this.id, "mousedown",
17225                 this.handleMouseDown);
17226         Event.un(this.id, "touchstart",
17227                 this.handleMouseDown);
17228         this._domRef = null;
17229         this.DDM._remove(this);
17230     },
17231
17232     destroy : function(){
17233         this.unreg();
17234     },
17235
17236     /**
17237      * Returns true if this instance is locked, or the drag drop mgr is locked
17238      * (meaning that all drag/drop is disabled on the page.)
17239      * @method isLocked
17240      * @return {boolean} true if this obj or all drag/drop is locked, else
17241      * false
17242      */
17243     isLocked: function() {
17244         return (this.DDM.isLocked() || this.locked);
17245     },
17246
17247     /**
17248      * Fired when this object is clicked
17249      * @method handleMouseDown
17250      * @param {Event} e
17251      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17252      * @private
17253      */
17254     handleMouseDown: function(e, oDD){
17255      
17256         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17257             //Roo.log('not touch/ button !=0');
17258             return;
17259         }
17260         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17261             return; // double touch..
17262         }
17263         
17264
17265         if (this.isLocked()) {
17266             //Roo.log('locked');
17267             return;
17268         }
17269
17270         this.DDM.refreshCache(this.groups);
17271 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17272         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17273         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17274             //Roo.log('no outer handes or not over target');
17275                 // do nothing.
17276         } else {
17277 //            Roo.log('check validator');
17278             if (this.clickValidator(e)) {
17279 //                Roo.log('validate success');
17280                 // set the initial element position
17281                 this.setStartPosition();
17282
17283
17284                 this.b4MouseDown(e);
17285                 this.onMouseDown(e);
17286
17287                 this.DDM.handleMouseDown(e, this);
17288
17289                 this.DDM.stopEvent(e);
17290             } else {
17291
17292
17293             }
17294         }
17295     },
17296
17297     clickValidator: function(e) {
17298         var target = e.getTarget();
17299         return ( this.isValidHandleChild(target) &&
17300                     (this.id == this.handleElId ||
17301                         this.DDM.handleWasClicked(target, this.id)) );
17302     },
17303
17304     /**
17305      * Allows you to specify a tag name that should not start a drag operation
17306      * when clicked.  This is designed to facilitate embedding links within a
17307      * drag handle that do something other than start the drag.
17308      * @method addInvalidHandleType
17309      * @param {string} tagName the type of element to exclude
17310      */
17311     addInvalidHandleType: function(tagName) {
17312         var type = tagName.toUpperCase();
17313         this.invalidHandleTypes[type] = type;
17314     },
17315
17316     /**
17317      * Lets you to specify an element id for a child of a drag handle
17318      * that should not initiate a drag
17319      * @method addInvalidHandleId
17320      * @param {string} id the element id of the element you wish to ignore
17321      */
17322     addInvalidHandleId: function(id) {
17323         if (typeof id !== "string") {
17324             id = Roo.id(id);
17325         }
17326         this.invalidHandleIds[id] = id;
17327     },
17328
17329     /**
17330      * Lets you specify a css class of elements that will not initiate a drag
17331      * @method addInvalidHandleClass
17332      * @param {string} cssClass the class of the elements you wish to ignore
17333      */
17334     addInvalidHandleClass: function(cssClass) {
17335         this.invalidHandleClasses.push(cssClass);
17336     },
17337
17338     /**
17339      * Unsets an excluded tag name set by addInvalidHandleType
17340      * @method removeInvalidHandleType
17341      * @param {string} tagName the type of element to unexclude
17342      */
17343     removeInvalidHandleType: function(tagName) {
17344         var type = tagName.toUpperCase();
17345         // this.invalidHandleTypes[type] = null;
17346         delete this.invalidHandleTypes[type];
17347     },
17348
17349     /**
17350      * Unsets an invalid handle id
17351      * @method removeInvalidHandleId
17352      * @param {string} id the id of the element to re-enable
17353      */
17354     removeInvalidHandleId: function(id) {
17355         if (typeof id !== "string") {
17356             id = Roo.id(id);
17357         }
17358         delete this.invalidHandleIds[id];
17359     },
17360
17361     /**
17362      * Unsets an invalid css class
17363      * @method removeInvalidHandleClass
17364      * @param {string} cssClass the class of the element(s) you wish to
17365      * re-enable
17366      */
17367     removeInvalidHandleClass: function(cssClass) {
17368         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17369             if (this.invalidHandleClasses[i] == cssClass) {
17370                 delete this.invalidHandleClasses[i];
17371             }
17372         }
17373     },
17374
17375     /**
17376      * Checks the tag exclusion list to see if this click should be ignored
17377      * @method isValidHandleChild
17378      * @param {HTMLElement} node the HTMLElement to evaluate
17379      * @return {boolean} true if this is a valid tag type, false if not
17380      */
17381     isValidHandleChild: function(node) {
17382
17383         var valid = true;
17384         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17385         var nodeName;
17386         try {
17387             nodeName = node.nodeName.toUpperCase();
17388         } catch(e) {
17389             nodeName = node.nodeName;
17390         }
17391         valid = valid && !this.invalidHandleTypes[nodeName];
17392         valid = valid && !this.invalidHandleIds[node.id];
17393
17394         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17395             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17396         }
17397
17398
17399         return valid;
17400
17401     },
17402
17403     /**
17404      * Create the array of horizontal tick marks if an interval was specified
17405      * in setXConstraint().
17406      * @method setXTicks
17407      * @private
17408      */
17409     setXTicks: function(iStartX, iTickSize) {
17410         this.xTicks = [];
17411         this.xTickSize = iTickSize;
17412
17413         var tickMap = {};
17414
17415         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17416             if (!tickMap[i]) {
17417                 this.xTicks[this.xTicks.length] = i;
17418                 tickMap[i] = true;
17419             }
17420         }
17421
17422         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17423             if (!tickMap[i]) {
17424                 this.xTicks[this.xTicks.length] = i;
17425                 tickMap[i] = true;
17426             }
17427         }
17428
17429         this.xTicks.sort(this.DDM.numericSort) ;
17430     },
17431
17432     /**
17433      * Create the array of vertical tick marks if an interval was specified in
17434      * setYConstraint().
17435      * @method setYTicks
17436      * @private
17437      */
17438     setYTicks: function(iStartY, iTickSize) {
17439         this.yTicks = [];
17440         this.yTickSize = iTickSize;
17441
17442         var tickMap = {};
17443
17444         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17445             if (!tickMap[i]) {
17446                 this.yTicks[this.yTicks.length] = i;
17447                 tickMap[i] = true;
17448             }
17449         }
17450
17451         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17452             if (!tickMap[i]) {
17453                 this.yTicks[this.yTicks.length] = i;
17454                 tickMap[i] = true;
17455             }
17456         }
17457
17458         this.yTicks.sort(this.DDM.numericSort) ;
17459     },
17460
17461     /**
17462      * By default, the element can be dragged any place on the screen.  Use
17463      * this method to limit the horizontal travel of the element.  Pass in
17464      * 0,0 for the parameters if you want to lock the drag to the y axis.
17465      * @method setXConstraint
17466      * @param {int} iLeft the number of pixels the element can move to the left
17467      * @param {int} iRight the number of pixels the element can move to the
17468      * right
17469      * @param {int} iTickSize optional parameter for specifying that the
17470      * element
17471      * should move iTickSize pixels at a time.
17472      */
17473     setXConstraint: function(iLeft, iRight, iTickSize) {
17474         this.leftConstraint = iLeft;
17475         this.rightConstraint = iRight;
17476
17477         this.minX = this.initPageX - iLeft;
17478         this.maxX = this.initPageX + iRight;
17479         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17480
17481         this.constrainX = true;
17482     },
17483
17484     /**
17485      * Clears any constraints applied to this instance.  Also clears ticks
17486      * since they can't exist independent of a constraint at this time.
17487      * @method clearConstraints
17488      */
17489     clearConstraints: function() {
17490         this.constrainX = false;
17491         this.constrainY = false;
17492         this.clearTicks();
17493     },
17494
17495     /**
17496      * Clears any tick interval defined for this instance
17497      * @method clearTicks
17498      */
17499     clearTicks: function() {
17500         this.xTicks = null;
17501         this.yTicks = null;
17502         this.xTickSize = 0;
17503         this.yTickSize = 0;
17504     },
17505
17506     /**
17507      * By default, the element can be dragged any place on the screen.  Set
17508      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17509      * parameters if you want to lock the drag to the x axis.
17510      * @method setYConstraint
17511      * @param {int} iUp the number of pixels the element can move up
17512      * @param {int} iDown the number of pixels the element can move down
17513      * @param {int} iTickSize optional parameter for specifying that the
17514      * element should move iTickSize pixels at a time.
17515      */
17516     setYConstraint: function(iUp, iDown, iTickSize) {
17517         this.topConstraint = iUp;
17518         this.bottomConstraint = iDown;
17519
17520         this.minY = this.initPageY - iUp;
17521         this.maxY = this.initPageY + iDown;
17522         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17523
17524         this.constrainY = true;
17525
17526     },
17527
17528     /**
17529      * resetConstraints must be called if you manually reposition a dd element.
17530      * @method resetConstraints
17531      * @param {boolean} maintainOffset
17532      */
17533     resetConstraints: function() {
17534
17535
17536         // Maintain offsets if necessary
17537         if (this.initPageX || this.initPageX === 0) {
17538             // figure out how much this thing has moved
17539             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17540             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17541
17542             this.setInitPosition(dx, dy);
17543
17544         // This is the first time we have detected the element's position
17545         } else {
17546             this.setInitPosition();
17547         }
17548
17549         if (this.constrainX) {
17550             this.setXConstraint( this.leftConstraint,
17551                                  this.rightConstraint,
17552                                  this.xTickSize        );
17553         }
17554
17555         if (this.constrainY) {
17556             this.setYConstraint( this.topConstraint,
17557                                  this.bottomConstraint,
17558                                  this.yTickSize         );
17559         }
17560     },
17561
17562     /**
17563      * Normally the drag element is moved pixel by pixel, but we can specify
17564      * that it move a number of pixels at a time.  This method resolves the
17565      * location when we have it set up like this.
17566      * @method getTick
17567      * @param {int} val where we want to place the object
17568      * @param {int[]} tickArray sorted array of valid points
17569      * @return {int} the closest tick
17570      * @private
17571      */
17572     getTick: function(val, tickArray) {
17573
17574         if (!tickArray) {
17575             // If tick interval is not defined, it is effectively 1 pixel,
17576             // so we return the value passed to us.
17577             return val;
17578         } else if (tickArray[0] >= val) {
17579             // The value is lower than the first tick, so we return the first
17580             // tick.
17581             return tickArray[0];
17582         } else {
17583             for (var i=0, len=tickArray.length; i<len; ++i) {
17584                 var next = i + 1;
17585                 if (tickArray[next] && tickArray[next] >= val) {
17586                     var diff1 = val - tickArray[i];
17587                     var diff2 = tickArray[next] - val;
17588                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17589                 }
17590             }
17591
17592             // The value is larger than the last tick, so we return the last
17593             // tick.
17594             return tickArray[tickArray.length - 1];
17595         }
17596     },
17597
17598     /**
17599      * toString method
17600      * @method toString
17601      * @return {string} string representation of the dd obj
17602      */
17603     toString: function() {
17604         return ("DragDrop " + this.id);
17605     }
17606
17607 });
17608
17609 })();
17610 /*
17611  * Based on:
17612  * Ext JS Library 1.1.1
17613  * Copyright(c) 2006-2007, Ext JS, LLC.
17614  *
17615  * Originally Released Under LGPL - original licence link has changed is not relivant.
17616  *
17617  * Fork - LGPL
17618  * <script type="text/javascript">
17619  */
17620
17621
17622 /**
17623  * The drag and drop utility provides a framework for building drag and drop
17624  * applications.  In addition to enabling drag and drop for specific elements,
17625  * the drag and drop elements are tracked by the manager class, and the
17626  * interactions between the various elements are tracked during the drag and
17627  * the implementing code is notified about these important moments.
17628  */
17629
17630 // Only load the library once.  Rewriting the manager class would orphan
17631 // existing drag and drop instances.
17632 if (!Roo.dd.DragDropMgr) {
17633
17634 /**
17635  * @class Roo.dd.DragDropMgr
17636  * DragDropMgr is a singleton that tracks the element interaction for
17637  * all DragDrop items in the window.  Generally, you will not call
17638  * this class directly, but it does have helper methods that could
17639  * be useful in your DragDrop implementations.
17640  * @singleton
17641  */
17642 Roo.dd.DragDropMgr = function() {
17643
17644     var Event = Roo.EventManager;
17645
17646     return {
17647
17648         /**
17649          * Two dimensional Array of registered DragDrop objects.  The first
17650          * dimension is the DragDrop item group, the second the DragDrop
17651          * object.
17652          * @property ids
17653          * @type {string: string}
17654          * @private
17655          * @static
17656          */
17657         ids: {},
17658
17659         /**
17660          * Array of element ids defined as drag handles.  Used to determine
17661          * if the element that generated the mousedown event is actually the
17662          * handle and not the html element itself.
17663          * @property handleIds
17664          * @type {string: string}
17665          * @private
17666          * @static
17667          */
17668         handleIds: {},
17669
17670         /**
17671          * the DragDrop object that is currently being dragged
17672          * @property dragCurrent
17673          * @type DragDrop
17674          * @private
17675          * @static
17676          **/
17677         dragCurrent: null,
17678
17679         /**
17680          * the DragDrop object(s) that are being hovered over
17681          * @property dragOvers
17682          * @type Array
17683          * @private
17684          * @static
17685          */
17686         dragOvers: {},
17687
17688         /**
17689          * the X distance between the cursor and the object being dragged
17690          * @property deltaX
17691          * @type int
17692          * @private
17693          * @static
17694          */
17695         deltaX: 0,
17696
17697         /**
17698          * the Y distance between the cursor and the object being dragged
17699          * @property deltaY
17700          * @type int
17701          * @private
17702          * @static
17703          */
17704         deltaY: 0,
17705
17706         /**
17707          * Flag to determine if we should prevent the default behavior of the
17708          * events we define. By default this is true, but this can be set to
17709          * false if you need the default behavior (not recommended)
17710          * @property preventDefault
17711          * @type boolean
17712          * @static
17713          */
17714         preventDefault: true,
17715
17716         /**
17717          * Flag to determine if we should stop the propagation of the events
17718          * we generate. This is true by default but you may want to set it to
17719          * false if the html element contains other features that require the
17720          * mouse click.
17721          * @property stopPropagation
17722          * @type boolean
17723          * @static
17724          */
17725         stopPropagation: true,
17726
17727         /**
17728          * Internal flag that is set to true when drag and drop has been
17729          * intialized
17730          * @property initialized
17731          * @private
17732          * @static
17733          */
17734         initalized: false,
17735
17736         /**
17737          * All drag and drop can be disabled.
17738          * @property locked
17739          * @private
17740          * @static
17741          */
17742         locked: false,
17743
17744         /**
17745          * Called the first time an element is registered.
17746          * @method init
17747          * @private
17748          * @static
17749          */
17750         init: function() {
17751             this.initialized = true;
17752         },
17753
17754         /**
17755          * In point mode, drag and drop interaction is defined by the
17756          * location of the cursor during the drag/drop
17757          * @property POINT
17758          * @type int
17759          * @static
17760          */
17761         POINT: 0,
17762
17763         /**
17764          * In intersect mode, drag and drop interactio nis defined by the
17765          * overlap of two or more drag and drop objects.
17766          * @property INTERSECT
17767          * @type int
17768          * @static
17769          */
17770         INTERSECT: 1,
17771
17772         /**
17773          * The current drag and drop mode.  Default: POINT
17774          * @property mode
17775          * @type int
17776          * @static
17777          */
17778         mode: 0,
17779
17780         /**
17781          * Runs method on all drag and drop objects
17782          * @method _execOnAll
17783          * @private
17784          * @static
17785          */
17786         _execOnAll: function(sMethod, args) {
17787             for (var i in this.ids) {
17788                 for (var j in this.ids[i]) {
17789                     var oDD = this.ids[i][j];
17790                     if (! this.isTypeOfDD(oDD)) {
17791                         continue;
17792                     }
17793                     oDD[sMethod].apply(oDD, args);
17794                 }
17795             }
17796         },
17797
17798         /**
17799          * Drag and drop initialization.  Sets up the global event handlers
17800          * @method _onLoad
17801          * @private
17802          * @static
17803          */
17804         _onLoad: function() {
17805
17806             this.init();
17807
17808             if (!Roo.isTouch) {
17809                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17810                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17811             }
17812             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17813             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17814             
17815             Event.on(window,   "unload",    this._onUnload, this, true);
17816             Event.on(window,   "resize",    this._onResize, this, true);
17817             // Event.on(window,   "mouseout",    this._test);
17818
17819         },
17820
17821         /**
17822          * Reset constraints on all drag and drop objs
17823          * @method _onResize
17824          * @private
17825          * @static
17826          */
17827         _onResize: function(e) {
17828             this._execOnAll("resetConstraints", []);
17829         },
17830
17831         /**
17832          * Lock all drag and drop functionality
17833          * @method lock
17834          * @static
17835          */
17836         lock: function() { this.locked = true; },
17837
17838         /**
17839          * Unlock all drag and drop functionality
17840          * @method unlock
17841          * @static
17842          */
17843         unlock: function() { this.locked = false; },
17844
17845         /**
17846          * Is drag and drop locked?
17847          * @method isLocked
17848          * @return {boolean} True if drag and drop is locked, false otherwise.
17849          * @static
17850          */
17851         isLocked: function() { return this.locked; },
17852
17853         /**
17854          * Location cache that is set for all drag drop objects when a drag is
17855          * initiated, cleared when the drag is finished.
17856          * @property locationCache
17857          * @private
17858          * @static
17859          */
17860         locationCache: {},
17861
17862         /**
17863          * Set useCache to false if you want to force object the lookup of each
17864          * drag and drop linked element constantly during a drag.
17865          * @property useCache
17866          * @type boolean
17867          * @static
17868          */
17869         useCache: true,
17870
17871         /**
17872          * The number of pixels that the mouse needs to move after the
17873          * mousedown before the drag is initiated.  Default=3;
17874          * @property clickPixelThresh
17875          * @type int
17876          * @static
17877          */
17878         clickPixelThresh: 3,
17879
17880         /**
17881          * The number of milliseconds after the mousedown event to initiate the
17882          * drag if we don't get a mouseup event. Default=1000
17883          * @property clickTimeThresh
17884          * @type int
17885          * @static
17886          */
17887         clickTimeThresh: 350,
17888
17889         /**
17890          * Flag that indicates that either the drag pixel threshold or the
17891          * mousdown time threshold has been met
17892          * @property dragThreshMet
17893          * @type boolean
17894          * @private
17895          * @static
17896          */
17897         dragThreshMet: false,
17898
17899         /**
17900          * Timeout used for the click time threshold
17901          * @property clickTimeout
17902          * @type Object
17903          * @private
17904          * @static
17905          */
17906         clickTimeout: null,
17907
17908         /**
17909          * The X position of the mousedown event stored for later use when a
17910          * drag threshold is met.
17911          * @property startX
17912          * @type int
17913          * @private
17914          * @static
17915          */
17916         startX: 0,
17917
17918         /**
17919          * The Y position of the mousedown event stored for later use when a
17920          * drag threshold is met.
17921          * @property startY
17922          * @type int
17923          * @private
17924          * @static
17925          */
17926         startY: 0,
17927
17928         /**
17929          * Each DragDrop instance must be registered with the DragDropMgr.
17930          * This is executed in DragDrop.init()
17931          * @method regDragDrop
17932          * @param {DragDrop} oDD the DragDrop object to register
17933          * @param {String} sGroup the name of the group this element belongs to
17934          * @static
17935          */
17936         regDragDrop: function(oDD, sGroup) {
17937             if (!this.initialized) { this.init(); }
17938
17939             if (!this.ids[sGroup]) {
17940                 this.ids[sGroup] = {};
17941             }
17942             this.ids[sGroup][oDD.id] = oDD;
17943         },
17944
17945         /**
17946          * Removes the supplied dd instance from the supplied group. Executed
17947          * by DragDrop.removeFromGroup, so don't call this function directly.
17948          * @method removeDDFromGroup
17949          * @private
17950          * @static
17951          */
17952         removeDDFromGroup: function(oDD, sGroup) {
17953             if (!this.ids[sGroup]) {
17954                 this.ids[sGroup] = {};
17955             }
17956
17957             var obj = this.ids[sGroup];
17958             if (obj && obj[oDD.id]) {
17959                 delete obj[oDD.id];
17960             }
17961         },
17962
17963         /**
17964          * Unregisters a drag and drop item.  This is executed in
17965          * DragDrop.unreg, use that method instead of calling this directly.
17966          * @method _remove
17967          * @private
17968          * @static
17969          */
17970         _remove: function(oDD) {
17971             for (var g in oDD.groups) {
17972                 if (g && this.ids[g][oDD.id]) {
17973                     delete this.ids[g][oDD.id];
17974                 }
17975             }
17976             delete this.handleIds[oDD.id];
17977         },
17978
17979         /**
17980          * Each DragDrop handle element must be registered.  This is done
17981          * automatically when executing DragDrop.setHandleElId()
17982          * @method regHandle
17983          * @param {String} sDDId the DragDrop id this element is a handle for
17984          * @param {String} sHandleId the id of the element that is the drag
17985          * handle
17986          * @static
17987          */
17988         regHandle: function(sDDId, sHandleId) {
17989             if (!this.handleIds[sDDId]) {
17990                 this.handleIds[sDDId] = {};
17991             }
17992             this.handleIds[sDDId][sHandleId] = sHandleId;
17993         },
17994
17995         /**
17996          * Utility function to determine if a given element has been
17997          * registered as a drag drop item.
17998          * @method isDragDrop
17999          * @param {String} id the element id to check
18000          * @return {boolean} true if this element is a DragDrop item,
18001          * false otherwise
18002          * @static
18003          */
18004         isDragDrop: function(id) {
18005             return ( this.getDDById(id) ) ? true : false;
18006         },
18007
18008         /**
18009          * Returns the drag and drop instances that are in all groups the
18010          * passed in instance belongs to.
18011          * @method getRelated
18012          * @param {DragDrop} p_oDD the obj to get related data for
18013          * @param {boolean} bTargetsOnly if true, only return targetable objs
18014          * @return {DragDrop[]} the related instances
18015          * @static
18016          */
18017         getRelated: function(p_oDD, bTargetsOnly) {
18018             var oDDs = [];
18019             for (var i in p_oDD.groups) {
18020                 for (j in this.ids[i]) {
18021                     var dd = this.ids[i][j];
18022                     if (! this.isTypeOfDD(dd)) {
18023                         continue;
18024                     }
18025                     if (!bTargetsOnly || dd.isTarget) {
18026                         oDDs[oDDs.length] = dd;
18027                     }
18028                 }
18029             }
18030
18031             return oDDs;
18032         },
18033
18034         /**
18035          * Returns true if the specified dd target is a legal target for
18036          * the specifice drag obj
18037          * @method isLegalTarget
18038          * @param {DragDrop} the drag obj
18039          * @param {DragDrop} the target
18040          * @return {boolean} true if the target is a legal target for the
18041          * dd obj
18042          * @static
18043          */
18044         isLegalTarget: function (oDD, oTargetDD) {
18045             var targets = this.getRelated(oDD, true);
18046             for (var i=0, len=targets.length;i<len;++i) {
18047                 if (targets[i].id == oTargetDD.id) {
18048                     return true;
18049                 }
18050             }
18051
18052             return false;
18053         },
18054
18055         /**
18056          * My goal is to be able to transparently determine if an object is
18057          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18058          * returns "object", oDD.constructor.toString() always returns
18059          * "DragDrop" and not the name of the subclass.  So for now it just
18060          * evaluates a well-known variable in DragDrop.
18061          * @method isTypeOfDD
18062          * @param {Object} the object to evaluate
18063          * @return {boolean} true if typeof oDD = DragDrop
18064          * @static
18065          */
18066         isTypeOfDD: function (oDD) {
18067             return (oDD && oDD.__ygDragDrop);
18068         },
18069
18070         /**
18071          * Utility function to determine if a given element has been
18072          * registered as a drag drop handle for the given Drag Drop object.
18073          * @method isHandle
18074          * @param {String} id the element id to check
18075          * @return {boolean} true if this element is a DragDrop handle, false
18076          * otherwise
18077          * @static
18078          */
18079         isHandle: function(sDDId, sHandleId) {
18080             return ( this.handleIds[sDDId] &&
18081                             this.handleIds[sDDId][sHandleId] );
18082         },
18083
18084         /**
18085          * Returns the DragDrop instance for a given id
18086          * @method getDDById
18087          * @param {String} id the id of the DragDrop object
18088          * @return {DragDrop} the drag drop object, null if it is not found
18089          * @static
18090          */
18091         getDDById: function(id) {
18092             for (var i in this.ids) {
18093                 if (this.ids[i][id]) {
18094                     return this.ids[i][id];
18095                 }
18096             }
18097             return null;
18098         },
18099
18100         /**
18101          * Fired after a registered DragDrop object gets the mousedown event.
18102          * Sets up the events required to track the object being dragged
18103          * @method handleMouseDown
18104          * @param {Event} e the event
18105          * @param oDD the DragDrop object being dragged
18106          * @private
18107          * @static
18108          */
18109         handleMouseDown: function(e, oDD) {
18110             if(Roo.QuickTips){
18111                 Roo.QuickTips.disable();
18112             }
18113             this.currentTarget = e.getTarget();
18114
18115             this.dragCurrent = oDD;
18116
18117             var el = oDD.getEl();
18118
18119             // track start position
18120             this.startX = e.getPageX();
18121             this.startY = e.getPageY();
18122
18123             this.deltaX = this.startX - el.offsetLeft;
18124             this.deltaY = this.startY - el.offsetTop;
18125
18126             this.dragThreshMet = false;
18127
18128             this.clickTimeout = setTimeout(
18129                     function() {
18130                         var DDM = Roo.dd.DDM;
18131                         DDM.startDrag(DDM.startX, DDM.startY);
18132                     },
18133                     this.clickTimeThresh );
18134         },
18135
18136         /**
18137          * Fired when either the drag pixel threshol or the mousedown hold
18138          * time threshold has been met.
18139          * @method startDrag
18140          * @param x {int} the X position of the original mousedown
18141          * @param y {int} the Y position of the original mousedown
18142          * @static
18143          */
18144         startDrag: function(x, y) {
18145             clearTimeout(this.clickTimeout);
18146             if (this.dragCurrent) {
18147                 this.dragCurrent.b4StartDrag(x, y);
18148                 this.dragCurrent.startDrag(x, y);
18149             }
18150             this.dragThreshMet = true;
18151         },
18152
18153         /**
18154          * Internal function to handle the mouseup event.  Will be invoked
18155          * from the context of the document.
18156          * @method handleMouseUp
18157          * @param {Event} e the event
18158          * @private
18159          * @static
18160          */
18161         handleMouseUp: function(e) {
18162
18163             if(Roo.QuickTips){
18164                 Roo.QuickTips.enable();
18165             }
18166             if (! this.dragCurrent) {
18167                 return;
18168             }
18169
18170             clearTimeout(this.clickTimeout);
18171
18172             if (this.dragThreshMet) {
18173                 this.fireEvents(e, true);
18174             } else {
18175             }
18176
18177             this.stopDrag(e);
18178
18179             this.stopEvent(e);
18180         },
18181
18182         /**
18183          * Utility to stop event propagation and event default, if these
18184          * features are turned on.
18185          * @method stopEvent
18186          * @param {Event} e the event as returned by this.getEvent()
18187          * @static
18188          */
18189         stopEvent: function(e){
18190             if(this.stopPropagation) {
18191                 e.stopPropagation();
18192             }
18193
18194             if (this.preventDefault) {
18195                 e.preventDefault();
18196             }
18197         },
18198
18199         /**
18200          * Internal function to clean up event handlers after the drag
18201          * operation is complete
18202          * @method stopDrag
18203          * @param {Event} e the event
18204          * @private
18205          * @static
18206          */
18207         stopDrag: function(e) {
18208             // Fire the drag end event for the item that was dragged
18209             if (this.dragCurrent) {
18210                 if (this.dragThreshMet) {
18211                     this.dragCurrent.b4EndDrag(e);
18212                     this.dragCurrent.endDrag(e);
18213                 }
18214
18215                 this.dragCurrent.onMouseUp(e);
18216             }
18217
18218             this.dragCurrent = null;
18219             this.dragOvers = {};
18220         },
18221
18222         /**
18223          * Internal function to handle the mousemove event.  Will be invoked
18224          * from the context of the html element.
18225          *
18226          * @TODO figure out what we can do about mouse events lost when the
18227          * user drags objects beyond the window boundary.  Currently we can
18228          * detect this in internet explorer by verifying that the mouse is
18229          * down during the mousemove event.  Firefox doesn't give us the
18230          * button state on the mousemove event.
18231          * @method handleMouseMove
18232          * @param {Event} e the event
18233          * @private
18234          * @static
18235          */
18236         handleMouseMove: function(e) {
18237             if (! this.dragCurrent) {
18238                 return true;
18239             }
18240
18241             // var button = e.which || e.button;
18242
18243             // check for IE mouseup outside of page boundary
18244             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18245                 this.stopEvent(e);
18246                 return this.handleMouseUp(e);
18247             }
18248
18249             if (!this.dragThreshMet) {
18250                 var diffX = Math.abs(this.startX - e.getPageX());
18251                 var diffY = Math.abs(this.startY - e.getPageY());
18252                 if (diffX > this.clickPixelThresh ||
18253                             diffY > this.clickPixelThresh) {
18254                     this.startDrag(this.startX, this.startY);
18255                 }
18256             }
18257
18258             if (this.dragThreshMet) {
18259                 this.dragCurrent.b4Drag(e);
18260                 this.dragCurrent.onDrag(e);
18261                 if(!this.dragCurrent.moveOnly){
18262                     this.fireEvents(e, false);
18263                 }
18264             }
18265
18266             this.stopEvent(e);
18267
18268             return true;
18269         },
18270
18271         /**
18272          * Iterates over all of the DragDrop elements to find ones we are
18273          * hovering over or dropping on
18274          * @method fireEvents
18275          * @param {Event} e the event
18276          * @param {boolean} isDrop is this a drop op or a mouseover op?
18277          * @private
18278          * @static
18279          */
18280         fireEvents: function(e, isDrop) {
18281             var dc = this.dragCurrent;
18282
18283             // If the user did the mouse up outside of the window, we could
18284             // get here even though we have ended the drag.
18285             if (!dc || dc.isLocked()) {
18286                 return;
18287             }
18288
18289             var pt = e.getPoint();
18290
18291             // cache the previous dragOver array
18292             var oldOvers = [];
18293
18294             var outEvts   = [];
18295             var overEvts  = [];
18296             var dropEvts  = [];
18297             var enterEvts = [];
18298
18299             // Check to see if the object(s) we were hovering over is no longer
18300             // being hovered over so we can fire the onDragOut event
18301             for (var i in this.dragOvers) {
18302
18303                 var ddo = this.dragOvers[i];
18304
18305                 if (! this.isTypeOfDD(ddo)) {
18306                     continue;
18307                 }
18308
18309                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18310                     outEvts.push( ddo );
18311                 }
18312
18313                 oldOvers[i] = true;
18314                 delete this.dragOvers[i];
18315             }
18316
18317             for (var sGroup in dc.groups) {
18318
18319                 if ("string" != typeof sGroup) {
18320                     continue;
18321                 }
18322
18323                 for (i in this.ids[sGroup]) {
18324                     var oDD = this.ids[sGroup][i];
18325                     if (! this.isTypeOfDD(oDD)) {
18326                         continue;
18327                     }
18328
18329                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18330                         if (this.isOverTarget(pt, oDD, this.mode)) {
18331                             // look for drop interactions
18332                             if (isDrop) {
18333                                 dropEvts.push( oDD );
18334                             // look for drag enter and drag over interactions
18335                             } else {
18336
18337                                 // initial drag over: dragEnter fires
18338                                 if (!oldOvers[oDD.id]) {
18339                                     enterEvts.push( oDD );
18340                                 // subsequent drag overs: dragOver fires
18341                                 } else {
18342                                     overEvts.push( oDD );
18343                                 }
18344
18345                                 this.dragOvers[oDD.id] = oDD;
18346                             }
18347                         }
18348                     }
18349                 }
18350             }
18351
18352             if (this.mode) {
18353                 if (outEvts.length) {
18354                     dc.b4DragOut(e, outEvts);
18355                     dc.onDragOut(e, outEvts);
18356                 }
18357
18358                 if (enterEvts.length) {
18359                     dc.onDragEnter(e, enterEvts);
18360                 }
18361
18362                 if (overEvts.length) {
18363                     dc.b4DragOver(e, overEvts);
18364                     dc.onDragOver(e, overEvts);
18365                 }
18366
18367                 if (dropEvts.length) {
18368                     dc.b4DragDrop(e, dropEvts);
18369                     dc.onDragDrop(e, dropEvts);
18370                 }
18371
18372             } else {
18373                 // fire dragout events
18374                 var len = 0;
18375                 for (i=0, len=outEvts.length; i<len; ++i) {
18376                     dc.b4DragOut(e, outEvts[i].id);
18377                     dc.onDragOut(e, outEvts[i].id);
18378                 }
18379
18380                 // fire enter events
18381                 for (i=0,len=enterEvts.length; i<len; ++i) {
18382                     // dc.b4DragEnter(e, oDD.id);
18383                     dc.onDragEnter(e, enterEvts[i].id);
18384                 }
18385
18386                 // fire over events
18387                 for (i=0,len=overEvts.length; i<len; ++i) {
18388                     dc.b4DragOver(e, overEvts[i].id);
18389                     dc.onDragOver(e, overEvts[i].id);
18390                 }
18391
18392                 // fire drop events
18393                 for (i=0, len=dropEvts.length; i<len; ++i) {
18394                     dc.b4DragDrop(e, dropEvts[i].id);
18395                     dc.onDragDrop(e, dropEvts[i].id);
18396                 }
18397
18398             }
18399
18400             // notify about a drop that did not find a target
18401             if (isDrop && !dropEvts.length) {
18402                 dc.onInvalidDrop(e);
18403             }
18404
18405         },
18406
18407         /**
18408          * Helper function for getting the best match from the list of drag
18409          * and drop objects returned by the drag and drop events when we are
18410          * in INTERSECT mode.  It returns either the first object that the
18411          * cursor is over, or the object that has the greatest overlap with
18412          * the dragged element.
18413          * @method getBestMatch
18414          * @param  {DragDrop[]} dds The array of drag and drop objects
18415          * targeted
18416          * @return {DragDrop}       The best single match
18417          * @static
18418          */
18419         getBestMatch: function(dds) {
18420             var winner = null;
18421             // Return null if the input is not what we expect
18422             //if (!dds || !dds.length || dds.length == 0) {
18423                // winner = null;
18424             // If there is only one item, it wins
18425             //} else if (dds.length == 1) {
18426
18427             var len = dds.length;
18428
18429             if (len == 1) {
18430                 winner = dds[0];
18431             } else {
18432                 // Loop through the targeted items
18433                 for (var i=0; i<len; ++i) {
18434                     var dd = dds[i];
18435                     // If the cursor is over the object, it wins.  If the
18436                     // cursor is over multiple matches, the first one we come
18437                     // to wins.
18438                     if (dd.cursorIsOver) {
18439                         winner = dd;
18440                         break;
18441                     // Otherwise the object with the most overlap wins
18442                     } else {
18443                         if (!winner ||
18444                             winner.overlap.getArea() < dd.overlap.getArea()) {
18445                             winner = dd;
18446                         }
18447                     }
18448                 }
18449             }
18450
18451             return winner;
18452         },
18453
18454         /**
18455          * Refreshes the cache of the top-left and bottom-right points of the
18456          * drag and drop objects in the specified group(s).  This is in the
18457          * format that is stored in the drag and drop instance, so typical
18458          * usage is:
18459          * <code>
18460          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18461          * </code>
18462          * Alternatively:
18463          * <code>
18464          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18465          * </code>
18466          * @TODO this really should be an indexed array.  Alternatively this
18467          * method could accept both.
18468          * @method refreshCache
18469          * @param {Object} groups an associative array of groups to refresh
18470          * @static
18471          */
18472         refreshCache: function(groups) {
18473             for (var sGroup in groups) {
18474                 if ("string" != typeof sGroup) {
18475                     continue;
18476                 }
18477                 for (var i in this.ids[sGroup]) {
18478                     var oDD = this.ids[sGroup][i];
18479
18480                     if (this.isTypeOfDD(oDD)) {
18481                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18482                         var loc = this.getLocation(oDD);
18483                         if (loc) {
18484                             this.locationCache[oDD.id] = loc;
18485                         } else {
18486                             delete this.locationCache[oDD.id];
18487                             // this will unregister the drag and drop object if
18488                             // the element is not in a usable state
18489                             // oDD.unreg();
18490                         }
18491                     }
18492                 }
18493             }
18494         },
18495
18496         /**
18497          * This checks to make sure an element exists and is in the DOM.  The
18498          * main purpose is to handle cases where innerHTML is used to remove
18499          * drag and drop objects from the DOM.  IE provides an 'unspecified
18500          * error' when trying to access the offsetParent of such an element
18501          * @method verifyEl
18502          * @param {HTMLElement} el the element to check
18503          * @return {boolean} true if the element looks usable
18504          * @static
18505          */
18506         verifyEl: function(el) {
18507             if (el) {
18508                 var parent;
18509                 if(Roo.isIE){
18510                     try{
18511                         parent = el.offsetParent;
18512                     }catch(e){}
18513                 }else{
18514                     parent = el.offsetParent;
18515                 }
18516                 if (parent) {
18517                     return true;
18518                 }
18519             }
18520
18521             return false;
18522         },
18523
18524         /**
18525          * Returns a Region object containing the drag and drop element's position
18526          * and size, including the padding configured for it
18527          * @method getLocation
18528          * @param {DragDrop} oDD the drag and drop object to get the
18529          *                       location for
18530          * @return {Roo.lib.Region} a Region object representing the total area
18531          *                             the element occupies, including any padding
18532          *                             the instance is configured for.
18533          * @static
18534          */
18535         getLocation: function(oDD) {
18536             if (! this.isTypeOfDD(oDD)) {
18537                 return null;
18538             }
18539
18540             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18541
18542             try {
18543                 pos= Roo.lib.Dom.getXY(el);
18544             } catch (e) { }
18545
18546             if (!pos) {
18547                 return null;
18548             }
18549
18550             x1 = pos[0];
18551             x2 = x1 + el.offsetWidth;
18552             y1 = pos[1];
18553             y2 = y1 + el.offsetHeight;
18554
18555             t = y1 - oDD.padding[0];
18556             r = x2 + oDD.padding[1];
18557             b = y2 + oDD.padding[2];
18558             l = x1 - oDD.padding[3];
18559
18560             return new Roo.lib.Region( t, r, b, l );
18561         },
18562
18563         /**
18564          * Checks the cursor location to see if it over the target
18565          * @method isOverTarget
18566          * @param {Roo.lib.Point} pt The point to evaluate
18567          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18568          * @return {boolean} true if the mouse is over the target
18569          * @private
18570          * @static
18571          */
18572         isOverTarget: function(pt, oTarget, intersect) {
18573             // use cache if available
18574             var loc = this.locationCache[oTarget.id];
18575             if (!loc || !this.useCache) {
18576                 loc = this.getLocation(oTarget);
18577                 this.locationCache[oTarget.id] = loc;
18578
18579             }
18580
18581             if (!loc) {
18582                 return false;
18583             }
18584
18585             oTarget.cursorIsOver = loc.contains( pt );
18586
18587             // DragDrop is using this as a sanity check for the initial mousedown
18588             // in this case we are done.  In POINT mode, if the drag obj has no
18589             // contraints, we are also done. Otherwise we need to evaluate the
18590             // location of the target as related to the actual location of the
18591             // dragged element.
18592             var dc = this.dragCurrent;
18593             if (!dc || !dc.getTargetCoord ||
18594                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18595                 return oTarget.cursorIsOver;
18596             }
18597
18598             oTarget.overlap = null;
18599
18600             // Get the current location of the drag element, this is the
18601             // location of the mouse event less the delta that represents
18602             // where the original mousedown happened on the element.  We
18603             // need to consider constraints and ticks as well.
18604             var pos = dc.getTargetCoord(pt.x, pt.y);
18605
18606             var el = dc.getDragEl();
18607             var curRegion = new Roo.lib.Region( pos.y,
18608                                                    pos.x + el.offsetWidth,
18609                                                    pos.y + el.offsetHeight,
18610                                                    pos.x );
18611
18612             var overlap = curRegion.intersect(loc);
18613
18614             if (overlap) {
18615                 oTarget.overlap = overlap;
18616                 return (intersect) ? true : oTarget.cursorIsOver;
18617             } else {
18618                 return false;
18619             }
18620         },
18621
18622         /**
18623          * unload event handler
18624          * @method _onUnload
18625          * @private
18626          * @static
18627          */
18628         _onUnload: function(e, me) {
18629             Roo.dd.DragDropMgr.unregAll();
18630         },
18631
18632         /**
18633          * Cleans up the drag and drop events and objects.
18634          * @method unregAll
18635          * @private
18636          * @static
18637          */
18638         unregAll: function() {
18639
18640             if (this.dragCurrent) {
18641                 this.stopDrag();
18642                 this.dragCurrent = null;
18643             }
18644
18645             this._execOnAll("unreg", []);
18646
18647             for (i in this.elementCache) {
18648                 delete this.elementCache[i];
18649             }
18650
18651             this.elementCache = {};
18652             this.ids = {};
18653         },
18654
18655         /**
18656          * A cache of DOM elements
18657          * @property elementCache
18658          * @private
18659          * @static
18660          */
18661         elementCache: {},
18662
18663         /**
18664          * Get the wrapper for the DOM element specified
18665          * @method getElWrapper
18666          * @param {String} id the id of the element to get
18667          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18668          * @private
18669          * @deprecated This wrapper isn't that useful
18670          * @static
18671          */
18672         getElWrapper: function(id) {
18673             var oWrapper = this.elementCache[id];
18674             if (!oWrapper || !oWrapper.el) {
18675                 oWrapper = this.elementCache[id] =
18676                     new this.ElementWrapper(Roo.getDom(id));
18677             }
18678             return oWrapper;
18679         },
18680
18681         /**
18682          * Returns the actual DOM element
18683          * @method getElement
18684          * @param {String} id the id of the elment to get
18685          * @return {Object} The element
18686          * @deprecated use Roo.getDom instead
18687          * @static
18688          */
18689         getElement: function(id) {
18690             return Roo.getDom(id);
18691         },
18692
18693         /**
18694          * Returns the style property for the DOM element (i.e.,
18695          * document.getElById(id).style)
18696          * @method getCss
18697          * @param {String} id the id of the elment to get
18698          * @return {Object} The style property of the element
18699          * @deprecated use Roo.getDom instead
18700          * @static
18701          */
18702         getCss: function(id) {
18703             var el = Roo.getDom(id);
18704             return (el) ? el.style : null;
18705         },
18706
18707         /**
18708          * Inner class for cached elements
18709          * @class DragDropMgr.ElementWrapper
18710          * @for DragDropMgr
18711          * @private
18712          * @deprecated
18713          */
18714         ElementWrapper: function(el) {
18715                 /**
18716                  * The element
18717                  * @property el
18718                  */
18719                 this.el = el || null;
18720                 /**
18721                  * The element id
18722                  * @property id
18723                  */
18724                 this.id = this.el && el.id;
18725                 /**
18726                  * A reference to the style property
18727                  * @property css
18728                  */
18729                 this.css = this.el && el.style;
18730             },
18731
18732         /**
18733          * Returns the X position of an html element
18734          * @method getPosX
18735          * @param el the element for which to get the position
18736          * @return {int} the X coordinate
18737          * @for DragDropMgr
18738          * @deprecated use Roo.lib.Dom.getX instead
18739          * @static
18740          */
18741         getPosX: function(el) {
18742             return Roo.lib.Dom.getX(el);
18743         },
18744
18745         /**
18746          * Returns the Y position of an html element
18747          * @method getPosY
18748          * @param el the element for which to get the position
18749          * @return {int} the Y coordinate
18750          * @deprecated use Roo.lib.Dom.getY instead
18751          * @static
18752          */
18753         getPosY: function(el) {
18754             return Roo.lib.Dom.getY(el);
18755         },
18756
18757         /**
18758          * Swap two nodes.  In IE, we use the native method, for others we
18759          * emulate the IE behavior
18760          * @method swapNode
18761          * @param n1 the first node to swap
18762          * @param n2 the other node to swap
18763          * @static
18764          */
18765         swapNode: function(n1, n2) {
18766             if (n1.swapNode) {
18767                 n1.swapNode(n2);
18768             } else {
18769                 var p = n2.parentNode;
18770                 var s = n2.nextSibling;
18771
18772                 if (s == n1) {
18773                     p.insertBefore(n1, n2);
18774                 } else if (n2 == n1.nextSibling) {
18775                     p.insertBefore(n2, n1);
18776                 } else {
18777                     n1.parentNode.replaceChild(n2, n1);
18778                     p.insertBefore(n1, s);
18779                 }
18780             }
18781         },
18782
18783         /**
18784          * Returns the current scroll position
18785          * @method getScroll
18786          * @private
18787          * @static
18788          */
18789         getScroll: function () {
18790             var t, l, dde=document.documentElement, db=document.body;
18791             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18792                 t = dde.scrollTop;
18793                 l = dde.scrollLeft;
18794             } else if (db) {
18795                 t = db.scrollTop;
18796                 l = db.scrollLeft;
18797             } else {
18798
18799             }
18800             return { top: t, left: l };
18801         },
18802
18803         /**
18804          * Returns the specified element style property
18805          * @method getStyle
18806          * @param {HTMLElement} el          the element
18807          * @param {string}      styleProp   the style property
18808          * @return {string} The value of the style property
18809          * @deprecated use Roo.lib.Dom.getStyle
18810          * @static
18811          */
18812         getStyle: function(el, styleProp) {
18813             return Roo.fly(el).getStyle(styleProp);
18814         },
18815
18816         /**
18817          * Gets the scrollTop
18818          * @method getScrollTop
18819          * @return {int} the document's scrollTop
18820          * @static
18821          */
18822         getScrollTop: function () { return this.getScroll().top; },
18823
18824         /**
18825          * Gets the scrollLeft
18826          * @method getScrollLeft
18827          * @return {int} the document's scrollTop
18828          * @static
18829          */
18830         getScrollLeft: function () { return this.getScroll().left; },
18831
18832         /**
18833          * Sets the x/y position of an element to the location of the
18834          * target element.
18835          * @method moveToEl
18836          * @param {HTMLElement} moveEl      The element to move
18837          * @param {HTMLElement} targetEl    The position reference element
18838          * @static
18839          */
18840         moveToEl: function (moveEl, targetEl) {
18841             var aCoord = Roo.lib.Dom.getXY(targetEl);
18842             Roo.lib.Dom.setXY(moveEl, aCoord);
18843         },
18844
18845         /**
18846          * Numeric array sort function
18847          * @method numericSort
18848          * @static
18849          */
18850         numericSort: function(a, b) { return (a - b); },
18851
18852         /**
18853          * Internal counter
18854          * @property _timeoutCount
18855          * @private
18856          * @static
18857          */
18858         _timeoutCount: 0,
18859
18860         /**
18861          * Trying to make the load order less important.  Without this we get
18862          * an error if this file is loaded before the Event Utility.
18863          * @method _addListeners
18864          * @private
18865          * @static
18866          */
18867         _addListeners: function() {
18868             var DDM = Roo.dd.DDM;
18869             if ( Roo.lib.Event && document ) {
18870                 DDM._onLoad();
18871             } else {
18872                 if (DDM._timeoutCount > 2000) {
18873                 } else {
18874                     setTimeout(DDM._addListeners, 10);
18875                     if (document && document.body) {
18876                         DDM._timeoutCount += 1;
18877                     }
18878                 }
18879             }
18880         },
18881
18882         /**
18883          * Recursively searches the immediate parent and all child nodes for
18884          * the handle element in order to determine wheter or not it was
18885          * clicked.
18886          * @method handleWasClicked
18887          * @param node the html element to inspect
18888          * @static
18889          */
18890         handleWasClicked: function(node, id) {
18891             if (this.isHandle(id, node.id)) {
18892                 return true;
18893             } else {
18894                 // check to see if this is a text node child of the one we want
18895                 var p = node.parentNode;
18896
18897                 while (p) {
18898                     if (this.isHandle(id, p.id)) {
18899                         return true;
18900                     } else {
18901                         p = p.parentNode;
18902                     }
18903                 }
18904             }
18905
18906             return false;
18907         }
18908
18909     };
18910
18911 }();
18912
18913 // shorter alias, save a few bytes
18914 Roo.dd.DDM = Roo.dd.DragDropMgr;
18915 Roo.dd.DDM._addListeners();
18916
18917 }/*
18918  * Based on:
18919  * Ext JS Library 1.1.1
18920  * Copyright(c) 2006-2007, Ext JS, LLC.
18921  *
18922  * Originally Released Under LGPL - original licence link has changed is not relivant.
18923  *
18924  * Fork - LGPL
18925  * <script type="text/javascript">
18926  */
18927
18928 /**
18929  * @class Roo.dd.DD
18930  * A DragDrop implementation where the linked element follows the
18931  * mouse cursor during a drag.
18932  * @extends Roo.dd.DragDrop
18933  * @constructor
18934  * @param {String} id the id of the linked element
18935  * @param {String} sGroup the group of related DragDrop items
18936  * @param {object} config an object containing configurable attributes
18937  *                Valid properties for DD:
18938  *                    scroll
18939  */
18940 Roo.dd.DD = function(id, sGroup, config) {
18941     if (id) {
18942         this.init(id, sGroup, config);
18943     }
18944 };
18945
18946 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18947
18948     /**
18949      * When set to true, the utility automatically tries to scroll the browser
18950      * window wehn a drag and drop element is dragged near the viewport boundary.
18951      * Defaults to true.
18952      * @property scroll
18953      * @type boolean
18954      */
18955     scroll: true,
18956
18957     /**
18958      * Sets the pointer offset to the distance between the linked element's top
18959      * left corner and the location the element was clicked
18960      * @method autoOffset
18961      * @param {int} iPageX the X coordinate of the click
18962      * @param {int} iPageY the Y coordinate of the click
18963      */
18964     autoOffset: function(iPageX, iPageY) {
18965         var x = iPageX - this.startPageX;
18966         var y = iPageY - this.startPageY;
18967         this.setDelta(x, y);
18968     },
18969
18970     /**
18971      * Sets the pointer offset.  You can call this directly to force the
18972      * offset to be in a particular location (e.g., pass in 0,0 to set it
18973      * to the center of the object)
18974      * @method setDelta
18975      * @param {int} iDeltaX the distance from the left
18976      * @param {int} iDeltaY the distance from the top
18977      */
18978     setDelta: function(iDeltaX, iDeltaY) {
18979         this.deltaX = iDeltaX;
18980         this.deltaY = iDeltaY;
18981     },
18982
18983     /**
18984      * Sets the drag element to the location of the mousedown or click event,
18985      * maintaining the cursor location relative to the location on the element
18986      * that was clicked.  Override this if you want to place the element in a
18987      * location other than where the cursor is.
18988      * @method setDragElPos
18989      * @param {int} iPageX the X coordinate of the mousedown or drag event
18990      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18991      */
18992     setDragElPos: function(iPageX, iPageY) {
18993         // the first time we do this, we are going to check to make sure
18994         // the element has css positioning
18995
18996         var el = this.getDragEl();
18997         this.alignElWithMouse(el, iPageX, iPageY);
18998     },
18999
19000     /**
19001      * Sets the element to the location of the mousedown or click event,
19002      * maintaining the cursor location relative to the location on the element
19003      * that was clicked.  Override this if you want to place the element in a
19004      * location other than where the cursor is.
19005      * @method alignElWithMouse
19006      * @param {HTMLElement} el the element to move
19007      * @param {int} iPageX the X coordinate of the mousedown or drag event
19008      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19009      */
19010     alignElWithMouse: function(el, iPageX, iPageY) {
19011         var oCoord = this.getTargetCoord(iPageX, iPageY);
19012         var fly = el.dom ? el : Roo.fly(el);
19013         if (!this.deltaSetXY) {
19014             var aCoord = [oCoord.x, oCoord.y];
19015             fly.setXY(aCoord);
19016             var newLeft = fly.getLeft(true);
19017             var newTop  = fly.getTop(true);
19018             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19019         } else {
19020             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19021         }
19022
19023         this.cachePosition(oCoord.x, oCoord.y);
19024         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19025         return oCoord;
19026     },
19027
19028     /**
19029      * Saves the most recent position so that we can reset the constraints and
19030      * tick marks on-demand.  We need to know this so that we can calculate the
19031      * number of pixels the element is offset from its original position.
19032      * @method cachePosition
19033      * @param iPageX the current x position (optional, this just makes it so we
19034      * don't have to look it up again)
19035      * @param iPageY the current y position (optional, this just makes it so we
19036      * don't have to look it up again)
19037      */
19038     cachePosition: function(iPageX, iPageY) {
19039         if (iPageX) {
19040             this.lastPageX = iPageX;
19041             this.lastPageY = iPageY;
19042         } else {
19043             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19044             this.lastPageX = aCoord[0];
19045             this.lastPageY = aCoord[1];
19046         }
19047     },
19048
19049     /**
19050      * Auto-scroll the window if the dragged object has been moved beyond the
19051      * visible window boundary.
19052      * @method autoScroll
19053      * @param {int} x the drag element's x position
19054      * @param {int} y the drag element's y position
19055      * @param {int} h the height of the drag element
19056      * @param {int} w the width of the drag element
19057      * @private
19058      */
19059     autoScroll: function(x, y, h, w) {
19060
19061         if (this.scroll) {
19062             // The client height
19063             var clientH = Roo.lib.Dom.getViewWidth();
19064
19065             // The client width
19066             var clientW = Roo.lib.Dom.getViewHeight();
19067
19068             // The amt scrolled down
19069             var st = this.DDM.getScrollTop();
19070
19071             // The amt scrolled right
19072             var sl = this.DDM.getScrollLeft();
19073
19074             // Location of the bottom of the element
19075             var bot = h + y;
19076
19077             // Location of the right of the element
19078             var right = w + x;
19079
19080             // The distance from the cursor to the bottom of the visible area,
19081             // adjusted so that we don't scroll if the cursor is beyond the
19082             // element drag constraints
19083             var toBot = (clientH + st - y - this.deltaY);
19084
19085             // The distance from the cursor to the right of the visible area
19086             var toRight = (clientW + sl - x - this.deltaX);
19087
19088
19089             // How close to the edge the cursor must be before we scroll
19090             // var thresh = (document.all) ? 100 : 40;
19091             var thresh = 40;
19092
19093             // How many pixels to scroll per autoscroll op.  This helps to reduce
19094             // clunky scrolling. IE is more sensitive about this ... it needs this
19095             // value to be higher.
19096             var scrAmt = (document.all) ? 80 : 30;
19097
19098             // Scroll down if we are near the bottom of the visible page and the
19099             // obj extends below the crease
19100             if ( bot > clientH && toBot < thresh ) {
19101                 window.scrollTo(sl, st + scrAmt);
19102             }
19103
19104             // Scroll up if the window is scrolled down and the top of the object
19105             // goes above the top border
19106             if ( y < st && st > 0 && y - st < thresh ) {
19107                 window.scrollTo(sl, st - scrAmt);
19108             }
19109
19110             // Scroll right if the obj is beyond the right border and the cursor is
19111             // near the border.
19112             if ( right > clientW && toRight < thresh ) {
19113                 window.scrollTo(sl + scrAmt, st);
19114             }
19115
19116             // Scroll left if the window has been scrolled to the right and the obj
19117             // extends past the left border
19118             if ( x < sl && sl > 0 && x - sl < thresh ) {
19119                 window.scrollTo(sl - scrAmt, st);
19120             }
19121         }
19122     },
19123
19124     /**
19125      * Finds the location the element should be placed if we want to move
19126      * it to where the mouse location less the click offset would place us.
19127      * @method getTargetCoord
19128      * @param {int} iPageX the X coordinate of the click
19129      * @param {int} iPageY the Y coordinate of the click
19130      * @return an object that contains the coordinates (Object.x and Object.y)
19131      * @private
19132      */
19133     getTargetCoord: function(iPageX, iPageY) {
19134
19135
19136         var x = iPageX - this.deltaX;
19137         var y = iPageY - this.deltaY;
19138
19139         if (this.constrainX) {
19140             if (x < this.minX) { x = this.minX; }
19141             if (x > this.maxX) { x = this.maxX; }
19142         }
19143
19144         if (this.constrainY) {
19145             if (y < this.minY) { y = this.minY; }
19146             if (y > this.maxY) { y = this.maxY; }
19147         }
19148
19149         x = this.getTick(x, this.xTicks);
19150         y = this.getTick(y, this.yTicks);
19151
19152
19153         return {x:x, y:y};
19154     },
19155
19156     /*
19157      * Sets up config options specific to this class. Overrides
19158      * Roo.dd.DragDrop, but all versions of this method through the
19159      * inheritance chain are called
19160      */
19161     applyConfig: function() {
19162         Roo.dd.DD.superclass.applyConfig.call(this);
19163         this.scroll = (this.config.scroll !== false);
19164     },
19165
19166     /*
19167      * Event that fires prior to the onMouseDown event.  Overrides
19168      * Roo.dd.DragDrop.
19169      */
19170     b4MouseDown: function(e) {
19171         // this.resetConstraints();
19172         this.autoOffset(e.getPageX(),
19173                             e.getPageY());
19174     },
19175
19176     /*
19177      * Event that fires prior to the onDrag event.  Overrides
19178      * Roo.dd.DragDrop.
19179      */
19180     b4Drag: function(e) {
19181         this.setDragElPos(e.getPageX(),
19182                             e.getPageY());
19183     },
19184
19185     toString: function() {
19186         return ("DD " + this.id);
19187     }
19188
19189     //////////////////////////////////////////////////////////////////////////
19190     // Debugging ygDragDrop events that can be overridden
19191     //////////////////////////////////////////////////////////////////////////
19192     /*
19193     startDrag: function(x, y) {
19194     },
19195
19196     onDrag: function(e) {
19197     },
19198
19199     onDragEnter: function(e, id) {
19200     },
19201
19202     onDragOver: function(e, id) {
19203     },
19204
19205     onDragOut: function(e, id) {
19206     },
19207
19208     onDragDrop: function(e, id) {
19209     },
19210
19211     endDrag: function(e) {
19212     }
19213
19214     */
19215
19216 });/*
19217  * Based on:
19218  * Ext JS Library 1.1.1
19219  * Copyright(c) 2006-2007, Ext JS, LLC.
19220  *
19221  * Originally Released Under LGPL - original licence link has changed is not relivant.
19222  *
19223  * Fork - LGPL
19224  * <script type="text/javascript">
19225  */
19226
19227 /**
19228  * @class Roo.dd.DDProxy
19229  * A DragDrop implementation that inserts an empty, bordered div into
19230  * the document that follows the cursor during drag operations.  At the time of
19231  * the click, the frame div is resized to the dimensions of the linked html
19232  * element, and moved to the exact location of the linked element.
19233  *
19234  * References to the "frame" element refer to the single proxy element that
19235  * was created to be dragged in place of all DDProxy elements on the
19236  * page.
19237  *
19238  * @extends Roo.dd.DD
19239  * @constructor
19240  * @param {String} id the id of the linked html element
19241  * @param {String} sGroup the group of related DragDrop objects
19242  * @param {object} config an object containing configurable attributes
19243  *                Valid properties for DDProxy in addition to those in DragDrop:
19244  *                   resizeFrame, centerFrame, dragElId
19245  */
19246 Roo.dd.DDProxy = function(id, sGroup, config) {
19247     if (id) {
19248         this.init(id, sGroup, config);
19249         this.initFrame();
19250     }
19251 };
19252
19253 /**
19254  * The default drag frame div id
19255  * @property Roo.dd.DDProxy.dragElId
19256  * @type String
19257  * @static
19258  */
19259 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19260
19261 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19262
19263     /**
19264      * By default we resize the drag frame to be the same size as the element
19265      * we want to drag (this is to get the frame effect).  We can turn it off
19266      * if we want a different behavior.
19267      * @property resizeFrame
19268      * @type boolean
19269      */
19270     resizeFrame: true,
19271
19272     /**
19273      * By default the frame is positioned exactly where the drag element is, so
19274      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19275      * you do not have constraints on the obj is to have the drag frame centered
19276      * around the cursor.  Set centerFrame to true for this effect.
19277      * @property centerFrame
19278      * @type boolean
19279      */
19280     centerFrame: false,
19281
19282     /**
19283      * Creates the proxy element if it does not yet exist
19284      * @method createFrame
19285      */
19286     createFrame: function() {
19287         var self = this;
19288         var body = document.body;
19289
19290         if (!body || !body.firstChild) {
19291             setTimeout( function() { self.createFrame(); }, 50 );
19292             return;
19293         }
19294
19295         var div = this.getDragEl();
19296
19297         if (!div) {
19298             div    = document.createElement("div");
19299             div.id = this.dragElId;
19300             var s  = div.style;
19301
19302             s.position   = "absolute";
19303             s.visibility = "hidden";
19304             s.cursor     = "move";
19305             s.border     = "2px solid #aaa";
19306             s.zIndex     = 999;
19307
19308             // appendChild can blow up IE if invoked prior to the window load event
19309             // while rendering a table.  It is possible there are other scenarios
19310             // that would cause this to happen as well.
19311             body.insertBefore(div, body.firstChild);
19312         }
19313     },
19314
19315     /**
19316      * Initialization for the drag frame element.  Must be called in the
19317      * constructor of all subclasses
19318      * @method initFrame
19319      */
19320     initFrame: function() {
19321         this.createFrame();
19322     },
19323
19324     applyConfig: function() {
19325         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19326
19327         this.resizeFrame = (this.config.resizeFrame !== false);
19328         this.centerFrame = (this.config.centerFrame);
19329         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19330     },
19331
19332     /**
19333      * Resizes the drag frame to the dimensions of the clicked object, positions
19334      * it over the object, and finally displays it
19335      * @method showFrame
19336      * @param {int} iPageX X click position
19337      * @param {int} iPageY Y click position
19338      * @private
19339      */
19340     showFrame: function(iPageX, iPageY) {
19341         var el = this.getEl();
19342         var dragEl = this.getDragEl();
19343         var s = dragEl.style;
19344
19345         this._resizeProxy();
19346
19347         if (this.centerFrame) {
19348             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19349                            Math.round(parseInt(s.height, 10)/2) );
19350         }
19351
19352         this.setDragElPos(iPageX, iPageY);
19353
19354         Roo.fly(dragEl).show();
19355     },
19356
19357     /**
19358      * The proxy is automatically resized to the dimensions of the linked
19359      * element when a drag is initiated, unless resizeFrame is set to false
19360      * @method _resizeProxy
19361      * @private
19362      */
19363     _resizeProxy: function() {
19364         if (this.resizeFrame) {
19365             var el = this.getEl();
19366             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19367         }
19368     },
19369
19370     // overrides Roo.dd.DragDrop
19371     b4MouseDown: function(e) {
19372         var x = e.getPageX();
19373         var y = e.getPageY();
19374         this.autoOffset(x, y);
19375         this.setDragElPos(x, y);
19376     },
19377
19378     // overrides Roo.dd.DragDrop
19379     b4StartDrag: function(x, y) {
19380         // show the drag frame
19381         this.showFrame(x, y);
19382     },
19383
19384     // overrides Roo.dd.DragDrop
19385     b4EndDrag: function(e) {
19386         Roo.fly(this.getDragEl()).hide();
19387     },
19388
19389     // overrides Roo.dd.DragDrop
19390     // By default we try to move the element to the last location of the frame.
19391     // This is so that the default behavior mirrors that of Roo.dd.DD.
19392     endDrag: function(e) {
19393
19394         var lel = this.getEl();
19395         var del = this.getDragEl();
19396
19397         // Show the drag frame briefly so we can get its position
19398         del.style.visibility = "";
19399
19400         this.beforeMove();
19401         // Hide the linked element before the move to get around a Safari
19402         // rendering bug.
19403         lel.style.visibility = "hidden";
19404         Roo.dd.DDM.moveToEl(lel, del);
19405         del.style.visibility = "hidden";
19406         lel.style.visibility = "";
19407
19408         this.afterDrag();
19409     },
19410
19411     beforeMove : function(){
19412
19413     },
19414
19415     afterDrag : function(){
19416
19417     },
19418
19419     toString: function() {
19420         return ("DDProxy " + this.id);
19421     }
19422
19423 });
19424 /*
19425  * Based on:
19426  * Ext JS Library 1.1.1
19427  * Copyright(c) 2006-2007, Ext JS, LLC.
19428  *
19429  * Originally Released Under LGPL - original licence link has changed is not relivant.
19430  *
19431  * Fork - LGPL
19432  * <script type="text/javascript">
19433  */
19434
19435  /**
19436  * @class Roo.dd.DDTarget
19437  * A DragDrop implementation that does not move, but can be a drop
19438  * target.  You would get the same result by simply omitting implementation
19439  * for the event callbacks, but this way we reduce the processing cost of the
19440  * event listener and the callbacks.
19441  * @extends Roo.dd.DragDrop
19442  * @constructor
19443  * @param {String} id the id of the element that is a drop target
19444  * @param {String} sGroup the group of related DragDrop objects
19445  * @param {object} config an object containing configurable attributes
19446  *                 Valid properties for DDTarget in addition to those in
19447  *                 DragDrop:
19448  *                    none
19449  */
19450 Roo.dd.DDTarget = function(id, sGroup, config) {
19451     if (id) {
19452         this.initTarget(id, sGroup, config);
19453     }
19454     if (config.listeners || config.events) { 
19455        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19456             listeners : config.listeners || {}, 
19457             events : config.events || {} 
19458         });    
19459     }
19460 };
19461
19462 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19463 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19464     toString: function() {
19465         return ("DDTarget " + this.id);
19466     }
19467 });
19468 /*
19469  * Based on:
19470  * Ext JS Library 1.1.1
19471  * Copyright(c) 2006-2007, Ext JS, LLC.
19472  *
19473  * Originally Released Under LGPL - original licence link has changed is not relivant.
19474  *
19475  * Fork - LGPL
19476  * <script type="text/javascript">
19477  */
19478  
19479
19480 /**
19481  * @class Roo.dd.ScrollManager
19482  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19483  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19484  * @singleton
19485  */
19486 Roo.dd.ScrollManager = function(){
19487     var ddm = Roo.dd.DragDropMgr;
19488     var els = {};
19489     var dragEl = null;
19490     var proc = {};
19491     
19492     
19493     
19494     var onStop = function(e){
19495         dragEl = null;
19496         clearProc();
19497     };
19498     
19499     var triggerRefresh = function(){
19500         if(ddm.dragCurrent){
19501              ddm.refreshCache(ddm.dragCurrent.groups);
19502         }
19503     };
19504     
19505     var doScroll = function(){
19506         if(ddm.dragCurrent){
19507             var dds = Roo.dd.ScrollManager;
19508             if(!dds.animate){
19509                 if(proc.el.scroll(proc.dir, dds.increment)){
19510                     triggerRefresh();
19511                 }
19512             }else{
19513                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19514             }
19515         }
19516     };
19517     
19518     var clearProc = function(){
19519         if(proc.id){
19520             clearInterval(proc.id);
19521         }
19522         proc.id = 0;
19523         proc.el = null;
19524         proc.dir = "";
19525     };
19526     
19527     var startProc = function(el, dir){
19528          Roo.log('scroll startproc');
19529         clearProc();
19530         proc.el = el;
19531         proc.dir = dir;
19532         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19533     };
19534     
19535     var onFire = function(e, isDrop){
19536        
19537         if(isDrop || !ddm.dragCurrent){ return; }
19538         var dds = Roo.dd.ScrollManager;
19539         if(!dragEl || dragEl != ddm.dragCurrent){
19540             dragEl = ddm.dragCurrent;
19541             // refresh regions on drag start
19542             dds.refreshCache();
19543         }
19544         
19545         var xy = Roo.lib.Event.getXY(e);
19546         var pt = new Roo.lib.Point(xy[0], xy[1]);
19547         for(var id in els){
19548             var el = els[id], r = el._region;
19549             if(r && r.contains(pt) && el.isScrollable()){
19550                 if(r.bottom - pt.y <= dds.thresh){
19551                     if(proc.el != el){
19552                         startProc(el, "down");
19553                     }
19554                     return;
19555                 }else if(r.right - pt.x <= dds.thresh){
19556                     if(proc.el != el){
19557                         startProc(el, "left");
19558                     }
19559                     return;
19560                 }else if(pt.y - r.top <= dds.thresh){
19561                     if(proc.el != el){
19562                         startProc(el, "up");
19563                     }
19564                     return;
19565                 }else if(pt.x - r.left <= dds.thresh){
19566                     if(proc.el != el){
19567                         startProc(el, "right");
19568                     }
19569                     return;
19570                 }
19571             }
19572         }
19573         clearProc();
19574     };
19575     
19576     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19577     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19578     
19579     return {
19580         /**
19581          * Registers new overflow element(s) to auto scroll
19582          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19583          */
19584         register : function(el){
19585             if(el instanceof Array){
19586                 for(var i = 0, len = el.length; i < len; i++) {
19587                         this.register(el[i]);
19588                 }
19589             }else{
19590                 el = Roo.get(el);
19591                 els[el.id] = el;
19592             }
19593             Roo.dd.ScrollManager.els = els;
19594         },
19595         
19596         /**
19597          * Unregisters overflow element(s) so they are no longer scrolled
19598          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19599          */
19600         unregister : function(el){
19601             if(el instanceof Array){
19602                 for(var i = 0, len = el.length; i < len; i++) {
19603                         this.unregister(el[i]);
19604                 }
19605             }else{
19606                 el = Roo.get(el);
19607                 delete els[el.id];
19608             }
19609         },
19610         
19611         /**
19612          * The number of pixels from the edge of a container the pointer needs to be to 
19613          * trigger scrolling (defaults to 25)
19614          * @type Number
19615          */
19616         thresh : 25,
19617         
19618         /**
19619          * The number of pixels to scroll in each scroll increment (defaults to 50)
19620          * @type Number
19621          */
19622         increment : 100,
19623         
19624         /**
19625          * The frequency of scrolls in milliseconds (defaults to 500)
19626          * @type Number
19627          */
19628         frequency : 500,
19629         
19630         /**
19631          * True to animate the scroll (defaults to true)
19632          * @type Boolean
19633          */
19634         animate: true,
19635         
19636         /**
19637          * The animation duration in seconds - 
19638          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19639          * @type Number
19640          */
19641         animDuration: .4,
19642         
19643         /**
19644          * Manually trigger a cache refresh.
19645          */
19646         refreshCache : function(){
19647             for(var id in els){
19648                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19649                     els[id]._region = els[id].getRegion();
19650                 }
19651             }
19652         }
19653     };
19654 }();/*
19655  * Based on:
19656  * Ext JS Library 1.1.1
19657  * Copyright(c) 2006-2007, Ext JS, LLC.
19658  *
19659  * Originally Released Under LGPL - original licence link has changed is not relivant.
19660  *
19661  * Fork - LGPL
19662  * <script type="text/javascript">
19663  */
19664  
19665
19666 /**
19667  * @class Roo.dd.Registry
19668  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19669  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19670  * @singleton
19671  */
19672 Roo.dd.Registry = function(){
19673     var elements = {}; 
19674     var handles = {}; 
19675     var autoIdSeed = 0;
19676
19677     var getId = function(el, autogen){
19678         if(typeof el == "string"){
19679             return el;
19680         }
19681         var id = el.id;
19682         if(!id && autogen !== false){
19683             id = "roodd-" + (++autoIdSeed);
19684             el.id = id;
19685         }
19686         return id;
19687     };
19688     
19689     return {
19690     /**
19691      * Register a drag drop element
19692      * @param {String|HTMLElement} element The id or DOM node to register
19693      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19694      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19695      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19696      * populated in the data object (if applicable):
19697      * <pre>
19698 Value      Description<br />
19699 ---------  ------------------------------------------<br />
19700 handles    Array of DOM nodes that trigger dragging<br />
19701            for the element being registered<br />
19702 isHandle   True if the element passed in triggers<br />
19703            dragging itself, else false
19704 </pre>
19705      */
19706         register : function(el, data){
19707             data = data || {};
19708             if(typeof el == "string"){
19709                 el = document.getElementById(el);
19710             }
19711             data.ddel = el;
19712             elements[getId(el)] = data;
19713             if(data.isHandle !== false){
19714                 handles[data.ddel.id] = data;
19715             }
19716             if(data.handles){
19717                 var hs = data.handles;
19718                 for(var i = 0, len = hs.length; i < len; i++){
19719                         handles[getId(hs[i])] = data;
19720                 }
19721             }
19722         },
19723
19724     /**
19725      * Unregister a drag drop element
19726      * @param {String|HTMLElement}  element The id or DOM node to unregister
19727      */
19728         unregister : function(el){
19729             var id = getId(el, false);
19730             var data = elements[id];
19731             if(data){
19732                 delete elements[id];
19733                 if(data.handles){
19734                     var hs = data.handles;
19735                     for(var i = 0, len = hs.length; i < len; i++){
19736                         delete handles[getId(hs[i], false)];
19737                     }
19738                 }
19739             }
19740         },
19741
19742     /**
19743      * Returns the handle registered for a DOM Node by id
19744      * @param {String|HTMLElement} id The DOM node or id to look up
19745      * @return {Object} handle The custom handle data
19746      */
19747         getHandle : function(id){
19748             if(typeof id != "string"){ // must be element?
19749                 id = id.id;
19750             }
19751             return handles[id];
19752         },
19753
19754     /**
19755      * Returns the handle that is registered for the DOM node that is the target of the event
19756      * @param {Event} e The event
19757      * @return {Object} handle The custom handle data
19758      */
19759         getHandleFromEvent : function(e){
19760             var t = Roo.lib.Event.getTarget(e);
19761             return t ? handles[t.id] : null;
19762         },
19763
19764     /**
19765      * Returns a custom data object that is registered for a DOM node by id
19766      * @param {String|HTMLElement} id The DOM node or id to look up
19767      * @return {Object} data The custom data
19768      */
19769         getTarget : function(id){
19770             if(typeof id != "string"){ // must be element?
19771                 id = id.id;
19772             }
19773             return elements[id];
19774         },
19775
19776     /**
19777      * Returns a custom data object that is registered for the DOM node that is the target of the event
19778      * @param {Event} e The event
19779      * @return {Object} data The custom data
19780      */
19781         getTargetFromEvent : function(e){
19782             var t = Roo.lib.Event.getTarget(e);
19783             return t ? elements[t.id] || handles[t.id] : null;
19784         }
19785     };
19786 }();/*
19787  * Based on:
19788  * Ext JS Library 1.1.1
19789  * Copyright(c) 2006-2007, Ext JS, LLC.
19790  *
19791  * Originally Released Under LGPL - original licence link has changed is not relivant.
19792  *
19793  * Fork - LGPL
19794  * <script type="text/javascript">
19795  */
19796  
19797
19798 /**
19799  * @class Roo.dd.StatusProxy
19800  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19801  * default drag proxy used by all Roo.dd components.
19802  * @constructor
19803  * @param {Object} config
19804  */
19805 Roo.dd.StatusProxy = function(config){
19806     Roo.apply(this, config);
19807     this.id = this.id || Roo.id();
19808     this.el = new Roo.Layer({
19809         dh: {
19810             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19811                 {tag: "div", cls: "x-dd-drop-icon"},
19812                 {tag: "div", cls: "x-dd-drag-ghost"}
19813             ]
19814         }, 
19815         shadow: !config || config.shadow !== false
19816     });
19817     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19818     this.dropStatus = this.dropNotAllowed;
19819 };
19820
19821 Roo.dd.StatusProxy.prototype = {
19822     /**
19823      * @cfg {String} dropAllowed
19824      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19825      */
19826     dropAllowed : "x-dd-drop-ok",
19827     /**
19828      * @cfg {String} dropNotAllowed
19829      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19830      */
19831     dropNotAllowed : "x-dd-drop-nodrop",
19832
19833     /**
19834      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19835      * over the current target element.
19836      * @param {String} cssClass The css class for the new drop status indicator image
19837      */
19838     setStatus : function(cssClass){
19839         cssClass = cssClass || this.dropNotAllowed;
19840         if(this.dropStatus != cssClass){
19841             this.el.replaceClass(this.dropStatus, cssClass);
19842             this.dropStatus = cssClass;
19843         }
19844     },
19845
19846     /**
19847      * Resets the status indicator to the default dropNotAllowed value
19848      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19849      */
19850     reset : function(clearGhost){
19851         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19852         this.dropStatus = this.dropNotAllowed;
19853         if(clearGhost){
19854             this.ghost.update("");
19855         }
19856     },
19857
19858     /**
19859      * Updates the contents of the ghost element
19860      * @param {String} html The html that will replace the current innerHTML of the ghost element
19861      */
19862     update : function(html){
19863         if(typeof html == "string"){
19864             this.ghost.update(html);
19865         }else{
19866             this.ghost.update("");
19867             html.style.margin = "0";
19868             this.ghost.dom.appendChild(html);
19869         }
19870         // ensure float = none set?? cant remember why though.
19871         var el = this.ghost.dom.firstChild;
19872                 if(el){
19873                         Roo.fly(el).setStyle('float', 'none');
19874                 }
19875     },
19876     
19877     /**
19878      * Returns the underlying proxy {@link Roo.Layer}
19879      * @return {Roo.Layer} el
19880     */
19881     getEl : function(){
19882         return this.el;
19883     },
19884
19885     /**
19886      * Returns the ghost element
19887      * @return {Roo.Element} el
19888      */
19889     getGhost : function(){
19890         return this.ghost;
19891     },
19892
19893     /**
19894      * Hides the proxy
19895      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19896      */
19897     hide : function(clear){
19898         this.el.hide();
19899         if(clear){
19900             this.reset(true);
19901         }
19902     },
19903
19904     /**
19905      * Stops the repair animation if it's currently running
19906      */
19907     stop : function(){
19908         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19909             this.anim.stop();
19910         }
19911     },
19912
19913     /**
19914      * Displays this proxy
19915      */
19916     show : function(){
19917         this.el.show();
19918     },
19919
19920     /**
19921      * Force the Layer to sync its shadow and shim positions to the element
19922      */
19923     sync : function(){
19924         this.el.sync();
19925     },
19926
19927     /**
19928      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19929      * invalid drop operation by the item being dragged.
19930      * @param {Array} xy The XY position of the element ([x, y])
19931      * @param {Function} callback The function to call after the repair is complete
19932      * @param {Object} scope The scope in which to execute the callback
19933      */
19934     repair : function(xy, callback, scope){
19935         this.callback = callback;
19936         this.scope = scope;
19937         if(xy && this.animRepair !== false){
19938             this.el.addClass("x-dd-drag-repair");
19939             this.el.hideUnders(true);
19940             this.anim = this.el.shift({
19941                 duration: this.repairDuration || .5,
19942                 easing: 'easeOut',
19943                 xy: xy,
19944                 stopFx: true,
19945                 callback: this.afterRepair,
19946                 scope: this
19947             });
19948         }else{
19949             this.afterRepair();
19950         }
19951     },
19952
19953     // private
19954     afterRepair : function(){
19955         this.hide(true);
19956         if(typeof this.callback == "function"){
19957             this.callback.call(this.scope || this);
19958         }
19959         this.callback = null;
19960         this.scope = null;
19961     }
19962 };/*
19963  * Based on:
19964  * Ext JS Library 1.1.1
19965  * Copyright(c) 2006-2007, Ext JS, LLC.
19966  *
19967  * Originally Released Under LGPL - original licence link has changed is not relivant.
19968  *
19969  * Fork - LGPL
19970  * <script type="text/javascript">
19971  */
19972
19973 /**
19974  * @class Roo.dd.DragSource
19975  * @extends Roo.dd.DDProxy
19976  * A simple class that provides the basic implementation needed to make any element draggable.
19977  * @constructor
19978  * @param {String/HTMLElement/Element} el The container element
19979  * @param {Object} config
19980  */
19981 Roo.dd.DragSource = function(el, config){
19982     this.el = Roo.get(el);
19983     this.dragData = {};
19984     
19985     Roo.apply(this, config);
19986     
19987     if(!this.proxy){
19988         this.proxy = new Roo.dd.StatusProxy();
19989     }
19990
19991     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19992           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19993     
19994     this.dragging = false;
19995 };
19996
19997 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19998     /**
19999      * @cfg {String} dropAllowed
20000      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20001      */
20002     dropAllowed : "x-dd-drop-ok",
20003     /**
20004      * @cfg {String} dropNotAllowed
20005      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20006      */
20007     dropNotAllowed : "x-dd-drop-nodrop",
20008
20009     /**
20010      * Returns the data object associated with this drag source
20011      * @return {Object} data An object containing arbitrary data
20012      */
20013     getDragData : function(e){
20014         return this.dragData;
20015     },
20016
20017     // private
20018     onDragEnter : function(e, id){
20019         var target = Roo.dd.DragDropMgr.getDDById(id);
20020         this.cachedTarget = target;
20021         if(this.beforeDragEnter(target, e, id) !== false){
20022             if(target.isNotifyTarget){
20023                 var status = target.notifyEnter(this, e, this.dragData);
20024                 this.proxy.setStatus(status);
20025             }else{
20026                 this.proxy.setStatus(this.dropAllowed);
20027             }
20028             
20029             if(this.afterDragEnter){
20030                 /**
20031                  * An empty function by default, but provided so that you can perform a custom action
20032                  * when the dragged item enters the drop target by providing an implementation.
20033                  * @param {Roo.dd.DragDrop} target The drop target
20034                  * @param {Event} e The event object
20035                  * @param {String} id The id of the dragged element
20036                  * @method afterDragEnter
20037                  */
20038                 this.afterDragEnter(target, e, id);
20039             }
20040         }
20041     },
20042
20043     /**
20044      * An empty function by default, but provided so that you can perform a custom action
20045      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20046      * @param {Roo.dd.DragDrop} target The drop target
20047      * @param {Event} e The event object
20048      * @param {String} id The id of the dragged element
20049      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20050      */
20051     beforeDragEnter : function(target, e, id){
20052         return true;
20053     },
20054
20055     // private
20056     alignElWithMouse: function() {
20057         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20058         this.proxy.sync();
20059     },
20060
20061     // private
20062     onDragOver : function(e, id){
20063         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20064         if(this.beforeDragOver(target, e, id) !== false){
20065             if(target.isNotifyTarget){
20066                 var status = target.notifyOver(this, e, this.dragData);
20067                 this.proxy.setStatus(status);
20068             }
20069
20070             if(this.afterDragOver){
20071                 /**
20072                  * An empty function by default, but provided so that you can perform a custom action
20073                  * while the dragged item is over the drop target by providing an implementation.
20074                  * @param {Roo.dd.DragDrop} target The drop target
20075                  * @param {Event} e The event object
20076                  * @param {String} id The id of the dragged element
20077                  * @method afterDragOver
20078                  */
20079                 this.afterDragOver(target, e, id);
20080             }
20081         }
20082     },
20083
20084     /**
20085      * An empty function by default, but provided so that you can perform a custom action
20086      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20087      * @param {Roo.dd.DragDrop} target The drop target
20088      * @param {Event} e The event object
20089      * @param {String} id The id of the dragged element
20090      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20091      */
20092     beforeDragOver : function(target, e, id){
20093         return true;
20094     },
20095
20096     // private
20097     onDragOut : function(e, id){
20098         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20099         if(this.beforeDragOut(target, e, id) !== false){
20100             if(target.isNotifyTarget){
20101                 target.notifyOut(this, e, this.dragData);
20102             }
20103             this.proxy.reset();
20104             if(this.afterDragOut){
20105                 /**
20106                  * An empty function by default, but provided so that you can perform a custom action
20107                  * after the dragged item is dragged out of the target without dropping.
20108                  * @param {Roo.dd.DragDrop} target The drop target
20109                  * @param {Event} e The event object
20110                  * @param {String} id The id of the dragged element
20111                  * @method afterDragOut
20112                  */
20113                 this.afterDragOut(target, e, id);
20114             }
20115         }
20116         this.cachedTarget = null;
20117     },
20118
20119     /**
20120      * An empty function by default, but provided so that you can perform a custom action before the dragged
20121      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20122      * @param {Roo.dd.DragDrop} target The drop target
20123      * @param {Event} e The event object
20124      * @param {String} id The id of the dragged element
20125      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20126      */
20127     beforeDragOut : function(target, e, id){
20128         return true;
20129     },
20130     
20131     // private
20132     onDragDrop : function(e, id){
20133         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20134         if(this.beforeDragDrop(target, e, id) !== false){
20135             if(target.isNotifyTarget){
20136                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20137                     this.onValidDrop(target, e, id);
20138                 }else{
20139                     this.onInvalidDrop(target, e, id);
20140                 }
20141             }else{
20142                 this.onValidDrop(target, e, id);
20143             }
20144             
20145             if(this.afterDragDrop){
20146                 /**
20147                  * An empty function by default, but provided so that you can perform a custom action
20148                  * after a valid drag drop has occurred by providing an implementation.
20149                  * @param {Roo.dd.DragDrop} target The drop target
20150                  * @param {Event} e The event object
20151                  * @param {String} id The id of the dropped element
20152                  * @method afterDragDrop
20153                  */
20154                 this.afterDragDrop(target, e, id);
20155             }
20156         }
20157         delete this.cachedTarget;
20158     },
20159
20160     /**
20161      * An empty function by default, but provided so that you can perform a custom action before the dragged
20162      * item is dropped onto the target and optionally cancel the onDragDrop.
20163      * @param {Roo.dd.DragDrop} target The drop target
20164      * @param {Event} e The event object
20165      * @param {String} id The id of the dragged element
20166      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20167      */
20168     beforeDragDrop : function(target, e, id){
20169         return true;
20170     },
20171
20172     // private
20173     onValidDrop : function(target, e, id){
20174         this.hideProxy();
20175         if(this.afterValidDrop){
20176             /**
20177              * An empty function by default, but provided so that you can perform a custom action
20178              * after a valid drop has occurred by providing an implementation.
20179              * @param {Object} target The target DD 
20180              * @param {Event} e The event object
20181              * @param {String} id The id of the dropped element
20182              * @method afterInvalidDrop
20183              */
20184             this.afterValidDrop(target, e, id);
20185         }
20186     },
20187
20188     // private
20189     getRepairXY : function(e, data){
20190         return this.el.getXY();  
20191     },
20192
20193     // private
20194     onInvalidDrop : function(target, e, id){
20195         this.beforeInvalidDrop(target, e, id);
20196         if(this.cachedTarget){
20197             if(this.cachedTarget.isNotifyTarget){
20198                 this.cachedTarget.notifyOut(this, e, this.dragData);
20199             }
20200             this.cacheTarget = null;
20201         }
20202         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20203
20204         if(this.afterInvalidDrop){
20205             /**
20206              * An empty function by default, but provided so that you can perform a custom action
20207              * after an invalid drop has occurred by providing an implementation.
20208              * @param {Event} e The event object
20209              * @param {String} id The id of the dropped element
20210              * @method afterInvalidDrop
20211              */
20212             this.afterInvalidDrop(e, id);
20213         }
20214     },
20215
20216     // private
20217     afterRepair : function(){
20218         if(Roo.enableFx){
20219             this.el.highlight(this.hlColor || "c3daf9");
20220         }
20221         this.dragging = false;
20222     },
20223
20224     /**
20225      * An empty function by default, but provided so that you can perform a custom action after an invalid
20226      * drop has occurred.
20227      * @param {Roo.dd.DragDrop} target The drop target
20228      * @param {Event} e The event object
20229      * @param {String} id The id of the dragged element
20230      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20231      */
20232     beforeInvalidDrop : function(target, e, id){
20233         return true;
20234     },
20235
20236     // private
20237     handleMouseDown : function(e){
20238         if(this.dragging) {
20239             return;
20240         }
20241         var data = this.getDragData(e);
20242         if(data && this.onBeforeDrag(data, e) !== false){
20243             this.dragData = data;
20244             this.proxy.stop();
20245             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20246         } 
20247     },
20248
20249     /**
20250      * An empty function by default, but provided so that you can perform a custom action before the initial
20251      * drag event begins and optionally cancel it.
20252      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20253      * @param {Event} e The event object
20254      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20255      */
20256     onBeforeDrag : function(data, e){
20257         return true;
20258     },
20259
20260     /**
20261      * An empty function by default, but provided so that you can perform a custom action once the initial
20262      * drag event has begun.  The drag cannot be canceled from this function.
20263      * @param {Number} x The x position of the click on the dragged object
20264      * @param {Number} y The y position of the click on the dragged object
20265      */
20266     onStartDrag : Roo.emptyFn,
20267
20268     // private - YUI override
20269     startDrag : function(x, y){
20270         this.proxy.reset();
20271         this.dragging = true;
20272         this.proxy.update("");
20273         this.onInitDrag(x, y);
20274         this.proxy.show();
20275     },
20276
20277     // private
20278     onInitDrag : function(x, y){
20279         var clone = this.el.dom.cloneNode(true);
20280         clone.id = Roo.id(); // prevent duplicate ids
20281         this.proxy.update(clone);
20282         this.onStartDrag(x, y);
20283         return true;
20284     },
20285
20286     /**
20287      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20288      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20289      */
20290     getProxy : function(){
20291         return this.proxy;  
20292     },
20293
20294     /**
20295      * Hides the drag source's {@link Roo.dd.StatusProxy}
20296      */
20297     hideProxy : function(){
20298         this.proxy.hide();  
20299         this.proxy.reset(true);
20300         this.dragging = false;
20301     },
20302
20303     // private
20304     triggerCacheRefresh : function(){
20305         Roo.dd.DDM.refreshCache(this.groups);
20306     },
20307
20308     // private - override to prevent hiding
20309     b4EndDrag: function(e) {
20310     },
20311
20312     // private - override to prevent moving
20313     endDrag : function(e){
20314         this.onEndDrag(this.dragData, e);
20315     },
20316
20317     // private
20318     onEndDrag : function(data, e){
20319     },
20320     
20321     // private - pin to cursor
20322     autoOffset : function(x, y) {
20323         this.setDelta(-12, -20);
20324     }    
20325 });/*
20326  * Based on:
20327  * Ext JS Library 1.1.1
20328  * Copyright(c) 2006-2007, Ext JS, LLC.
20329  *
20330  * Originally Released Under LGPL - original licence link has changed is not relivant.
20331  *
20332  * Fork - LGPL
20333  * <script type="text/javascript">
20334  */
20335
20336
20337 /**
20338  * @class Roo.dd.DropTarget
20339  * @extends Roo.dd.DDTarget
20340  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20341  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20342  * @constructor
20343  * @param {String/HTMLElement/Element} el The container element
20344  * @param {Object} config
20345  */
20346 Roo.dd.DropTarget = function(el, config){
20347     this.el = Roo.get(el);
20348     
20349     var listeners = false; ;
20350     if (config && config.listeners) {
20351         listeners= config.listeners;
20352         delete config.listeners;
20353     }
20354     Roo.apply(this, config);
20355     
20356     if(this.containerScroll){
20357         Roo.dd.ScrollManager.register(this.el);
20358     }
20359     this.addEvents( {
20360          /**
20361          * @scope Roo.dd.DropTarget
20362          */
20363          
20364          /**
20365          * @event enter
20366          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20367          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20368          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20369          * 
20370          * IMPORTANT : it should set this.overClass and this.dropAllowed
20371          * 
20372          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20373          * @param {Event} e The event
20374          * @param {Object} data An object containing arbitrary data supplied by the drag source
20375          */
20376         "enter" : true,
20377         
20378          /**
20379          * @event over
20380          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20381          * This method will be called on every mouse movement while the drag source is over the drop target.
20382          * This default implementation simply returns the dropAllowed config value.
20383          * 
20384          * IMPORTANT : it should set this.dropAllowed
20385          * 
20386          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20387          * @param {Event} e The event
20388          * @param {Object} data An object containing arbitrary data supplied by the drag source
20389          
20390          */
20391         "over" : true,
20392         /**
20393          * @event out
20394          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20395          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20396          * overClass (if any) from the drop element.
20397          * 
20398          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20399          * @param {Event} e The event
20400          * @param {Object} data An object containing arbitrary data supplied by the drag source
20401          */
20402          "out" : true,
20403          
20404         /**
20405          * @event drop
20406          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20407          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20408          * implementation that does something to process the drop event and returns true so that the drag source's
20409          * repair action does not run.
20410          * 
20411          * IMPORTANT : it should set this.success
20412          * 
20413          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20414          * @param {Event} e The event
20415          * @param {Object} data An object containing arbitrary data supplied by the drag source
20416         */
20417          "drop" : true
20418     });
20419             
20420      
20421     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20422         this.el.dom, 
20423         this.ddGroup || this.group,
20424         {
20425             isTarget: true,
20426             listeners : listeners || {} 
20427            
20428         
20429         }
20430     );
20431
20432 };
20433
20434 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20435     /**
20436      * @cfg {String} overClass
20437      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20438      */
20439      /**
20440      * @cfg {String} ddGroup
20441      * The drag drop group to handle drop events for
20442      */
20443      
20444     /**
20445      * @cfg {String} dropAllowed
20446      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20447      */
20448     dropAllowed : "x-dd-drop-ok",
20449     /**
20450      * @cfg {String} dropNotAllowed
20451      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20452      */
20453     dropNotAllowed : "x-dd-drop-nodrop",
20454     /**
20455      * @cfg {boolean} success
20456      * set this after drop listener.. 
20457      */
20458     success : false,
20459     /**
20460      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20461      * if the drop point is valid for over/enter..
20462      */
20463     valid : false,
20464     // private
20465     isTarget : true,
20466
20467     // private
20468     isNotifyTarget : true,
20469     
20470     /**
20471      * @hide
20472      */
20473     notifyEnter : function(dd, e, data)
20474     {
20475         this.valid = true;
20476         this.fireEvent('enter', dd, e, data);
20477         if(this.overClass){
20478             this.el.addClass(this.overClass);
20479         }
20480         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20481             this.valid ? this.dropAllowed : this.dropNotAllowed
20482         );
20483     },
20484
20485     /**
20486      * @hide
20487      */
20488     notifyOver : function(dd, e, data)
20489     {
20490         this.valid = true;
20491         this.fireEvent('over', dd, e, data);
20492         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20493             this.valid ? this.dropAllowed : this.dropNotAllowed
20494         );
20495     },
20496
20497     /**
20498      * @hide
20499      */
20500     notifyOut : function(dd, e, data)
20501     {
20502         this.fireEvent('out', dd, e, data);
20503         if(this.overClass){
20504             this.el.removeClass(this.overClass);
20505         }
20506     },
20507
20508     /**
20509      * @hide
20510      */
20511     notifyDrop : function(dd, e, data)
20512     {
20513         this.success = false;
20514         this.fireEvent('drop', dd, e, data);
20515         return this.success;
20516     }
20517 });/*
20518  * Based on:
20519  * Ext JS Library 1.1.1
20520  * Copyright(c) 2006-2007, Ext JS, LLC.
20521  *
20522  * Originally Released Under LGPL - original licence link has changed is not relivant.
20523  *
20524  * Fork - LGPL
20525  * <script type="text/javascript">
20526  */
20527
20528
20529 /**
20530  * @class Roo.dd.DragZone
20531  * @extends Roo.dd.DragSource
20532  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20533  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20534  * @constructor
20535  * @param {String/HTMLElement/Element} el The container element
20536  * @param {Object} config
20537  */
20538 Roo.dd.DragZone = function(el, config){
20539     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20540     if(this.containerScroll){
20541         Roo.dd.ScrollManager.register(this.el);
20542     }
20543 };
20544
20545 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20546     /**
20547      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20548      * for auto scrolling during drag operations.
20549      */
20550     /**
20551      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20552      * method after a failed drop (defaults to "c3daf9" - light blue)
20553      */
20554
20555     /**
20556      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20557      * for a valid target to drag based on the mouse down. Override this method
20558      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20559      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20560      * @param {EventObject} e The mouse down event
20561      * @return {Object} The dragData
20562      */
20563     getDragData : function(e){
20564         return Roo.dd.Registry.getHandleFromEvent(e);
20565     },
20566     
20567     /**
20568      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20569      * this.dragData.ddel
20570      * @param {Number} x The x position of the click on the dragged object
20571      * @param {Number} y The y position of the click on the dragged object
20572      * @return {Boolean} true to continue the drag, false to cancel
20573      */
20574     onInitDrag : function(x, y){
20575         this.proxy.update(this.dragData.ddel.cloneNode(true));
20576         this.onStartDrag(x, y);
20577         return true;
20578     },
20579     
20580     /**
20581      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20582      */
20583     afterRepair : function(){
20584         if(Roo.enableFx){
20585             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20586         }
20587         this.dragging = false;
20588     },
20589
20590     /**
20591      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20592      * the XY of this.dragData.ddel
20593      * @param {EventObject} e The mouse up event
20594      * @return {Array} The xy location (e.g. [100, 200])
20595      */
20596     getRepairXY : function(e){
20597         return Roo.Element.fly(this.dragData.ddel).getXY();  
20598     }
20599 });/*
20600  * Based on:
20601  * Ext JS Library 1.1.1
20602  * Copyright(c) 2006-2007, Ext JS, LLC.
20603  *
20604  * Originally Released Under LGPL - original licence link has changed is not relivant.
20605  *
20606  * Fork - LGPL
20607  * <script type="text/javascript">
20608  */
20609 /**
20610  * @class Roo.dd.DropZone
20611  * @extends Roo.dd.DropTarget
20612  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20613  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20614  * @constructor
20615  * @param {String/HTMLElement/Element} el The container element
20616  * @param {Object} config
20617  */
20618 Roo.dd.DropZone = function(el, config){
20619     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20620 };
20621
20622 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20623     /**
20624      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20625      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20626      * provide your own custom lookup.
20627      * @param {Event} e The event
20628      * @return {Object} data The custom data
20629      */
20630     getTargetFromEvent : function(e){
20631         return Roo.dd.Registry.getTargetFromEvent(e);
20632     },
20633
20634     /**
20635      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20636      * that it has registered.  This method has no default implementation and should be overridden to provide
20637      * node-specific processing if necessary.
20638      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20639      * {@link #getTargetFromEvent} for this node)
20640      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20641      * @param {Event} e The event
20642      * @param {Object} data An object containing arbitrary data supplied by the drag source
20643      */
20644     onNodeEnter : function(n, dd, e, data){
20645         
20646     },
20647
20648     /**
20649      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20650      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20651      * overridden to provide the proper feedback.
20652      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20653      * {@link #getTargetFromEvent} for this node)
20654      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20655      * @param {Event} e The event
20656      * @param {Object} data An object containing arbitrary data supplied by the drag source
20657      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20658      * underlying {@link Roo.dd.StatusProxy} can be updated
20659      */
20660     onNodeOver : function(n, dd, e, data){
20661         return this.dropAllowed;
20662     },
20663
20664     /**
20665      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20666      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20667      * node-specific processing if necessary.
20668      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20669      * {@link #getTargetFromEvent} for this node)
20670      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20671      * @param {Event} e The event
20672      * @param {Object} data An object containing arbitrary data supplied by the drag source
20673      */
20674     onNodeOut : function(n, dd, e, data){
20675         
20676     },
20677
20678     /**
20679      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20680      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20681      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20682      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20683      * {@link #getTargetFromEvent} for this node)
20684      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20685      * @param {Event} e The event
20686      * @param {Object} data An object containing arbitrary data supplied by the drag source
20687      * @return {Boolean} True if the drop was valid, else false
20688      */
20689     onNodeDrop : function(n, dd, e, data){
20690         return false;
20691     },
20692
20693     /**
20694      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20695      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20696      * it should be overridden to provide the proper feedback if necessary.
20697      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20698      * @param {Event} e The event
20699      * @param {Object} data An object containing arbitrary data supplied by the drag source
20700      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20701      * underlying {@link Roo.dd.StatusProxy} can be updated
20702      */
20703     onContainerOver : function(dd, e, data){
20704         return this.dropNotAllowed;
20705     },
20706
20707     /**
20708      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20709      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20710      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20711      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20712      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20713      * @param {Event} e The event
20714      * @param {Object} data An object containing arbitrary data supplied by the drag source
20715      * @return {Boolean} True if the drop was valid, else false
20716      */
20717     onContainerDrop : function(dd, e, data){
20718         return false;
20719     },
20720
20721     /**
20722      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20723      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20724      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20725      * you should override this method and provide a custom implementation.
20726      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20727      * @param {Event} e The event
20728      * @param {Object} data An object containing arbitrary data supplied by the drag source
20729      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20730      * underlying {@link Roo.dd.StatusProxy} can be updated
20731      */
20732     notifyEnter : function(dd, e, data){
20733         return this.dropNotAllowed;
20734     },
20735
20736     /**
20737      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20738      * This method will be called on every mouse movement while the drag source is over the drop zone.
20739      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20740      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20741      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20742      * registered node, it will call {@link #onContainerOver}.
20743      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20744      * @param {Event} e The event
20745      * @param {Object} data An object containing arbitrary data supplied by the drag source
20746      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20747      * underlying {@link Roo.dd.StatusProxy} can be updated
20748      */
20749     notifyOver : function(dd, e, data){
20750         var n = this.getTargetFromEvent(e);
20751         if(!n){ // not over valid drop target
20752             if(this.lastOverNode){
20753                 this.onNodeOut(this.lastOverNode, dd, e, data);
20754                 this.lastOverNode = null;
20755             }
20756             return this.onContainerOver(dd, e, data);
20757         }
20758         if(this.lastOverNode != n){
20759             if(this.lastOverNode){
20760                 this.onNodeOut(this.lastOverNode, dd, e, data);
20761             }
20762             this.onNodeEnter(n, dd, e, data);
20763             this.lastOverNode = n;
20764         }
20765         return this.onNodeOver(n, dd, e, data);
20766     },
20767
20768     /**
20769      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20770      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20771      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20772      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20773      * @param {Event} e The event
20774      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20775      */
20776     notifyOut : function(dd, e, data){
20777         if(this.lastOverNode){
20778             this.onNodeOut(this.lastOverNode, dd, e, data);
20779             this.lastOverNode = null;
20780         }
20781     },
20782
20783     /**
20784      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20785      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20786      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20787      * otherwise it will call {@link #onContainerDrop}.
20788      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20789      * @param {Event} e The event
20790      * @param {Object} data An object containing arbitrary data supplied by the drag source
20791      * @return {Boolean} True if the drop was valid, else false
20792      */
20793     notifyDrop : function(dd, e, data){
20794         if(this.lastOverNode){
20795             this.onNodeOut(this.lastOverNode, dd, e, data);
20796             this.lastOverNode = null;
20797         }
20798         var n = this.getTargetFromEvent(e);
20799         return n ?
20800             this.onNodeDrop(n, dd, e, data) :
20801             this.onContainerDrop(dd, e, data);
20802     },
20803
20804     // private
20805     triggerCacheRefresh : function(){
20806         Roo.dd.DDM.refreshCache(this.groups);
20807     }  
20808 });/*
20809  * Based on:
20810  * Ext JS Library 1.1.1
20811  * Copyright(c) 2006-2007, Ext JS, LLC.
20812  *
20813  * Originally Released Under LGPL - original licence link has changed is not relivant.
20814  *
20815  * Fork - LGPL
20816  * <script type="text/javascript">
20817  */
20818
20819
20820 /**
20821  * @class Roo.data.SortTypes
20822  * @singleton
20823  * Defines the default sorting (casting?) comparison functions used when sorting data.
20824  */
20825 Roo.data.SortTypes = {
20826     /**
20827      * Default sort that does nothing
20828      * @param {Mixed} s The value being converted
20829      * @return {Mixed} The comparison value
20830      */
20831     none : function(s){
20832         return s;
20833     },
20834     
20835     /**
20836      * The regular expression used to strip tags
20837      * @type {RegExp}
20838      * @property
20839      */
20840     stripTagsRE : /<\/?[^>]+>/gi,
20841     
20842     /**
20843      * Strips all HTML tags to sort on text only
20844      * @param {Mixed} s The value being converted
20845      * @return {String} The comparison value
20846      */
20847     asText : function(s){
20848         return String(s).replace(this.stripTagsRE, "");
20849     },
20850     
20851     /**
20852      * Strips all HTML tags to sort on text only - Case insensitive
20853      * @param {Mixed} s The value being converted
20854      * @return {String} The comparison value
20855      */
20856     asUCText : function(s){
20857         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20858     },
20859     
20860     /**
20861      * Case insensitive string
20862      * @param {Mixed} s The value being converted
20863      * @return {String} The comparison value
20864      */
20865     asUCString : function(s) {
20866         return String(s).toUpperCase();
20867     },
20868     
20869     /**
20870      * Date sorting
20871      * @param {Mixed} s The value being converted
20872      * @return {Number} The comparison value
20873      */
20874     asDate : function(s) {
20875         if(!s){
20876             return 0;
20877         }
20878         if(s instanceof Date){
20879             return s.getTime();
20880         }
20881         return Date.parse(String(s));
20882     },
20883     
20884     /**
20885      * Float sorting
20886      * @param {Mixed} s The value being converted
20887      * @return {Float} The comparison value
20888      */
20889     asFloat : function(s) {
20890         var val = parseFloat(String(s).replace(/,/g, ""));
20891         if(isNaN(val)) val = 0;
20892         return val;
20893     },
20894     
20895     /**
20896      * Integer sorting
20897      * @param {Mixed} s The value being converted
20898      * @return {Number} The comparison value
20899      */
20900     asInt : function(s) {
20901         var val = parseInt(String(s).replace(/,/g, ""));
20902         if(isNaN(val)) val = 0;
20903         return val;
20904     }
20905 };/*
20906  * Based on:
20907  * Ext JS Library 1.1.1
20908  * Copyright(c) 2006-2007, Ext JS, LLC.
20909  *
20910  * Originally Released Under LGPL - original licence link has changed is not relivant.
20911  *
20912  * Fork - LGPL
20913  * <script type="text/javascript">
20914  */
20915
20916 /**
20917 * @class Roo.data.Record
20918  * Instances of this class encapsulate both record <em>definition</em> information, and record
20919  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20920  * to access Records cached in an {@link Roo.data.Store} object.<br>
20921  * <p>
20922  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20923  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20924  * objects.<br>
20925  * <p>
20926  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20927  * @constructor
20928  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20929  * {@link #create}. The parameters are the same.
20930  * @param {Array} data An associative Array of data values keyed by the field name.
20931  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20932  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20933  * not specified an integer id is generated.
20934  */
20935 Roo.data.Record = function(data, id){
20936     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20937     this.data = data;
20938 };
20939
20940 /**
20941  * Generate a constructor for a specific record layout.
20942  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20943  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20944  * Each field definition object may contain the following properties: <ul>
20945  * <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,
20946  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20947  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20948  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20949  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20950  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20951  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20952  * this may be omitted.</p></li>
20953  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20954  * <ul><li>auto (Default, implies no conversion)</li>
20955  * <li>string</li>
20956  * <li>int</li>
20957  * <li>float</li>
20958  * <li>boolean</li>
20959  * <li>date</li></ul></p></li>
20960  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20961  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20962  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20963  * by the Reader into an object that will be stored in the Record. It is passed the
20964  * following parameters:<ul>
20965  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20966  * </ul></p></li>
20967  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20968  * </ul>
20969  * <br>usage:<br><pre><code>
20970 var TopicRecord = Roo.data.Record.create(
20971     {name: 'title', mapping: 'topic_title'},
20972     {name: 'author', mapping: 'username'},
20973     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20974     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20975     {name: 'lastPoster', mapping: 'user2'},
20976     {name: 'excerpt', mapping: 'post_text'}
20977 );
20978
20979 var myNewRecord = new TopicRecord({
20980     title: 'Do my job please',
20981     author: 'noobie',
20982     totalPosts: 1,
20983     lastPost: new Date(),
20984     lastPoster: 'Animal',
20985     excerpt: 'No way dude!'
20986 });
20987 myStore.add(myNewRecord);
20988 </code></pre>
20989  * @method create
20990  * @static
20991  */
20992 Roo.data.Record.create = function(o){
20993     var f = function(){
20994         f.superclass.constructor.apply(this, arguments);
20995     };
20996     Roo.extend(f, Roo.data.Record);
20997     var p = f.prototype;
20998     p.fields = new Roo.util.MixedCollection(false, function(field){
20999         return field.name;
21000     });
21001     for(var i = 0, len = o.length; i < len; i++){
21002         p.fields.add(new Roo.data.Field(o[i]));
21003     }
21004     f.getField = function(name){
21005         return p.fields.get(name);  
21006     };
21007     return f;
21008 };
21009
21010 Roo.data.Record.AUTO_ID = 1000;
21011 Roo.data.Record.EDIT = 'edit';
21012 Roo.data.Record.REJECT = 'reject';
21013 Roo.data.Record.COMMIT = 'commit';
21014
21015 Roo.data.Record.prototype = {
21016     /**
21017      * Readonly flag - true if this record has been modified.
21018      * @type Boolean
21019      */
21020     dirty : false,
21021     editing : false,
21022     error: null,
21023     modified: null,
21024
21025     // private
21026     join : function(store){
21027         this.store = store;
21028     },
21029
21030     /**
21031      * Set the named field to the specified value.
21032      * @param {String} name The name of the field to set.
21033      * @param {Object} value The value to set the field to.
21034      */
21035     set : function(name, value){
21036         if(this.data[name] == value){
21037             return;
21038         }
21039         this.dirty = true;
21040         if(!this.modified){
21041             this.modified = {};
21042         }
21043         if(typeof this.modified[name] == 'undefined'){
21044             this.modified[name] = this.data[name];
21045         }
21046         this.data[name] = value;
21047         if(!this.editing && this.store){
21048             this.store.afterEdit(this);
21049         }       
21050     },
21051
21052     /**
21053      * Get the value of the named field.
21054      * @param {String} name The name of the field to get the value of.
21055      * @return {Object} The value of the field.
21056      */
21057     get : function(name){
21058         return this.data[name]; 
21059     },
21060
21061     // private
21062     beginEdit : function(){
21063         this.editing = true;
21064         this.modified = {}; 
21065     },
21066
21067     // private
21068     cancelEdit : function(){
21069         this.editing = false;
21070         delete this.modified;
21071     },
21072
21073     // private
21074     endEdit : function(){
21075         this.editing = false;
21076         if(this.dirty && this.store){
21077             this.store.afterEdit(this);
21078         }
21079     },
21080
21081     /**
21082      * Usually called by the {@link Roo.data.Store} which owns the Record.
21083      * Rejects all changes made to the Record since either creation, or the last commit operation.
21084      * Modified fields are reverted to their original values.
21085      * <p>
21086      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21087      * of reject operations.
21088      */
21089     reject : function(){
21090         var m = this.modified;
21091         for(var n in m){
21092             if(typeof m[n] != "function"){
21093                 this.data[n] = m[n];
21094             }
21095         }
21096         this.dirty = false;
21097         delete this.modified;
21098         this.editing = false;
21099         if(this.store){
21100             this.store.afterReject(this);
21101         }
21102     },
21103
21104     /**
21105      * Usually called by the {@link Roo.data.Store} which owns the Record.
21106      * Commits all changes made to the Record since either creation, or the last commit operation.
21107      * <p>
21108      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21109      * of commit operations.
21110      */
21111     commit : function(){
21112         this.dirty = false;
21113         delete this.modified;
21114         this.editing = false;
21115         if(this.store){
21116             this.store.afterCommit(this);
21117         }
21118     },
21119
21120     // private
21121     hasError : function(){
21122         return this.error != null;
21123     },
21124
21125     // private
21126     clearError : function(){
21127         this.error = null;
21128     },
21129
21130     /**
21131      * Creates a copy of this record.
21132      * @param {String} id (optional) A new record id if you don't want to use this record's id
21133      * @return {Record}
21134      */
21135     copy : function(newId) {
21136         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21137     }
21138 };/*
21139  * Based on:
21140  * Ext JS Library 1.1.1
21141  * Copyright(c) 2006-2007, Ext JS, LLC.
21142  *
21143  * Originally Released Under LGPL - original licence link has changed is not relivant.
21144  *
21145  * Fork - LGPL
21146  * <script type="text/javascript">
21147  */
21148
21149
21150
21151 /**
21152  * @class Roo.data.Store
21153  * @extends Roo.util.Observable
21154  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21155  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21156  * <p>
21157  * 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
21158  * has no knowledge of the format of the data returned by the Proxy.<br>
21159  * <p>
21160  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21161  * instances from the data object. These records are cached and made available through accessor functions.
21162  * @constructor
21163  * Creates a new Store.
21164  * @param {Object} config A config object containing the objects needed for the Store to access data,
21165  * and read the data into Records.
21166  */
21167 Roo.data.Store = function(config){
21168     this.data = new Roo.util.MixedCollection(false);
21169     this.data.getKey = function(o){
21170         return o.id;
21171     };
21172     this.baseParams = {};
21173     // private
21174     this.paramNames = {
21175         "start" : "start",
21176         "limit" : "limit",
21177         "sort" : "sort",
21178         "dir" : "dir",
21179         "multisort" : "_multisort"
21180     };
21181
21182     if(config && config.data){
21183         this.inlineData = config.data;
21184         delete config.data;
21185     }
21186
21187     Roo.apply(this, config);
21188     
21189     if(this.reader){ // reader passed
21190         this.reader = Roo.factory(this.reader, Roo.data);
21191         this.reader.xmodule = this.xmodule || false;
21192         if(!this.recordType){
21193             this.recordType = this.reader.recordType;
21194         }
21195         if(this.reader.onMetaChange){
21196             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21197         }
21198     }
21199
21200     if(this.recordType){
21201         this.fields = this.recordType.prototype.fields;
21202     }
21203     this.modified = [];
21204
21205     this.addEvents({
21206         /**
21207          * @event datachanged
21208          * Fires when the data cache has changed, and a widget which is using this Store
21209          * as a Record cache should refresh its view.
21210          * @param {Store} this
21211          */
21212         datachanged : true,
21213         /**
21214          * @event metachange
21215          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21216          * @param {Store} this
21217          * @param {Object} meta The JSON metadata
21218          */
21219         metachange : true,
21220         /**
21221          * @event add
21222          * Fires when Records have been added to the Store
21223          * @param {Store} this
21224          * @param {Roo.data.Record[]} records The array of Records added
21225          * @param {Number} index The index at which the record(s) were added
21226          */
21227         add : true,
21228         /**
21229          * @event remove
21230          * Fires when a Record has been removed from the Store
21231          * @param {Store} this
21232          * @param {Roo.data.Record} record The Record that was removed
21233          * @param {Number} index The index at which the record was removed
21234          */
21235         remove : true,
21236         /**
21237          * @event update
21238          * Fires when a Record has been updated
21239          * @param {Store} this
21240          * @param {Roo.data.Record} record The Record that was updated
21241          * @param {String} operation The update operation being performed.  Value may be one of:
21242          * <pre><code>
21243  Roo.data.Record.EDIT
21244  Roo.data.Record.REJECT
21245  Roo.data.Record.COMMIT
21246          * </code></pre>
21247          */
21248         update : true,
21249         /**
21250          * @event clear
21251          * Fires when the data cache has been cleared.
21252          * @param {Store} this
21253          */
21254         clear : true,
21255         /**
21256          * @event beforeload
21257          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21258          * the load action will be canceled.
21259          * @param {Store} this
21260          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21261          */
21262         beforeload : true,
21263         /**
21264          * @event beforeloadadd
21265          * Fires after a new set of Records has been loaded.
21266          * @param {Store} this
21267          * @param {Roo.data.Record[]} records The Records that were loaded
21268          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21269          */
21270         beforeloadadd : true,
21271         /**
21272          * @event load
21273          * Fires after a new set of Records has been loaded, before they are added to the store.
21274          * @param {Store} this
21275          * @param {Roo.data.Record[]} records The Records that were loaded
21276          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21277          * @params {Object} return from reader
21278          */
21279         load : true,
21280         /**
21281          * @event loadexception
21282          * Fires if an exception occurs in the Proxy during loading.
21283          * Called with the signature of the Proxy's "loadexception" event.
21284          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21285          * 
21286          * @param {Proxy} 
21287          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21288          * @param {Object} load options 
21289          * @param {Object} jsonData from your request (normally this contains the Exception)
21290          */
21291         loadexception : true
21292     });
21293     
21294     if(this.proxy){
21295         this.proxy = Roo.factory(this.proxy, Roo.data);
21296         this.proxy.xmodule = this.xmodule || false;
21297         this.relayEvents(this.proxy,  ["loadexception"]);
21298     }
21299     this.sortToggle = {};
21300     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21301
21302     Roo.data.Store.superclass.constructor.call(this);
21303
21304     if(this.inlineData){
21305         this.loadData(this.inlineData);
21306         delete this.inlineData;
21307     }
21308 };
21309
21310 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21311      /**
21312     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21313     * without a remote query - used by combo/forms at present.
21314     */
21315     
21316     /**
21317     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21318     */
21319     /**
21320     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21321     */
21322     /**
21323     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21324     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21325     */
21326     /**
21327     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21328     * on any HTTP request
21329     */
21330     /**
21331     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21332     */
21333     /**
21334     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21335     */
21336     multiSort: false,
21337     /**
21338     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21339     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21340     */
21341     remoteSort : false,
21342
21343     /**
21344     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21345      * loaded or when a record is removed. (defaults to false).
21346     */
21347     pruneModifiedRecords : false,
21348
21349     // private
21350     lastOptions : null,
21351
21352     /**
21353      * Add Records to the Store and fires the add event.
21354      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21355      */
21356     add : function(records){
21357         records = [].concat(records);
21358         for(var i = 0, len = records.length; i < len; i++){
21359             records[i].join(this);
21360         }
21361         var index = this.data.length;
21362         this.data.addAll(records);
21363         this.fireEvent("add", this, records, index);
21364     },
21365
21366     /**
21367      * Remove a Record from the Store and fires the remove event.
21368      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21369      */
21370     remove : function(record){
21371         var index = this.data.indexOf(record);
21372         this.data.removeAt(index);
21373         if(this.pruneModifiedRecords){
21374             this.modified.remove(record);
21375         }
21376         this.fireEvent("remove", this, record, index);
21377     },
21378
21379     /**
21380      * Remove all Records from the Store and fires the clear event.
21381      */
21382     removeAll : function(){
21383         this.data.clear();
21384         if(this.pruneModifiedRecords){
21385             this.modified = [];
21386         }
21387         this.fireEvent("clear", this);
21388     },
21389
21390     /**
21391      * Inserts Records to the Store at the given index and fires the add event.
21392      * @param {Number} index The start index at which to insert the passed Records.
21393      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21394      */
21395     insert : function(index, records){
21396         records = [].concat(records);
21397         for(var i = 0, len = records.length; i < len; i++){
21398             this.data.insert(index, records[i]);
21399             records[i].join(this);
21400         }
21401         this.fireEvent("add", this, records, index);
21402     },
21403
21404     /**
21405      * Get the index within the cache of the passed Record.
21406      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21407      * @return {Number} The index of the passed Record. Returns -1 if not found.
21408      */
21409     indexOf : function(record){
21410         return this.data.indexOf(record);
21411     },
21412
21413     /**
21414      * Get the index within the cache of the Record with the passed id.
21415      * @param {String} id The id of the Record to find.
21416      * @return {Number} The index of the Record. Returns -1 if not found.
21417      */
21418     indexOfId : function(id){
21419         return this.data.indexOfKey(id);
21420     },
21421
21422     /**
21423      * Get the Record with the specified id.
21424      * @param {String} id The id of the Record to find.
21425      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21426      */
21427     getById : function(id){
21428         return this.data.key(id);
21429     },
21430
21431     /**
21432      * Get the Record at the specified index.
21433      * @param {Number} index The index of the Record to find.
21434      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21435      */
21436     getAt : function(index){
21437         return this.data.itemAt(index);
21438     },
21439
21440     /**
21441      * Returns a range of Records between specified indices.
21442      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21443      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21444      * @return {Roo.data.Record[]} An array of Records
21445      */
21446     getRange : function(start, end){
21447         return this.data.getRange(start, end);
21448     },
21449
21450     // private
21451     storeOptions : function(o){
21452         o = Roo.apply({}, o);
21453         delete o.callback;
21454         delete o.scope;
21455         this.lastOptions = o;
21456     },
21457
21458     /**
21459      * Loads the Record cache from the configured Proxy using the configured Reader.
21460      * <p>
21461      * If using remote paging, then the first load call must specify the <em>start</em>
21462      * and <em>limit</em> properties in the options.params property to establish the initial
21463      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21464      * <p>
21465      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21466      * and this call will return before the new data has been loaded. Perform any post-processing
21467      * in a callback function, or in a "load" event handler.</strong>
21468      * <p>
21469      * @param {Object} options An object containing properties which control loading options:<ul>
21470      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21471      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21472      * passed the following arguments:<ul>
21473      * <li>r : Roo.data.Record[]</li>
21474      * <li>options: Options object from the load call</li>
21475      * <li>success: Boolean success indicator</li></ul></li>
21476      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21477      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21478      * </ul>
21479      */
21480     load : function(options){
21481         options = options || {};
21482         if(this.fireEvent("beforeload", this, options) !== false){
21483             this.storeOptions(options);
21484             var p = Roo.apply(options.params || {}, this.baseParams);
21485             // if meta was not loaded from remote source.. try requesting it.
21486             if (!this.reader.metaFromRemote) {
21487                 p._requestMeta = 1;
21488             }
21489             if(this.sortInfo && this.remoteSort){
21490                 var pn = this.paramNames;
21491                 p[pn["sort"]] = this.sortInfo.field;
21492                 p[pn["dir"]] = this.sortInfo.direction;
21493             }
21494             if (this.multiSort) {
21495                 var pn = this.paramNames;
21496                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21497             }
21498             
21499             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21500         }
21501     },
21502
21503     /**
21504      * Reloads the Record cache from the configured Proxy using the configured Reader and
21505      * the options from the last load operation performed.
21506      * @param {Object} options (optional) An object containing properties which may override the options
21507      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21508      * the most recently used options are reused).
21509      */
21510     reload : function(options){
21511         this.load(Roo.applyIf(options||{}, this.lastOptions));
21512     },
21513
21514     // private
21515     // Called as a callback by the Reader during a load operation.
21516     loadRecords : function(o, options, success){
21517         if(!o || success === false){
21518             if(success !== false){
21519                 this.fireEvent("load", this, [], options, o);
21520             }
21521             if(options.callback){
21522                 options.callback.call(options.scope || this, [], options, false);
21523             }
21524             return;
21525         }
21526         // if data returned failure - throw an exception.
21527         if (o.success === false) {
21528             // show a message if no listener is registered.
21529             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21530                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21531             }
21532             // loadmask wil be hooked into this..
21533             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21534             return;
21535         }
21536         var r = o.records, t = o.totalRecords || r.length;
21537         
21538         this.fireEvent("beforeloadadd", this, r, options, o);
21539         
21540         if(!options || options.add !== true){
21541             if(this.pruneModifiedRecords){
21542                 this.modified = [];
21543             }
21544             for(var i = 0, len = r.length; i < len; i++){
21545                 r[i].join(this);
21546             }
21547             if(this.snapshot){
21548                 this.data = this.snapshot;
21549                 delete this.snapshot;
21550             }
21551             this.data.clear();
21552             this.data.addAll(r);
21553             this.totalLength = t;
21554             this.applySort();
21555             this.fireEvent("datachanged", this);
21556         }else{
21557             this.totalLength = Math.max(t, this.data.length+r.length);
21558             this.add(r);
21559         }
21560         this.fireEvent("load", this, r, options, o);
21561         if(options.callback){
21562             options.callback.call(options.scope || this, r, options, true);
21563         }
21564     },
21565
21566
21567     /**
21568      * Loads data from a passed data block. A Reader which understands the format of the data
21569      * must have been configured in the constructor.
21570      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21571      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21572      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21573      */
21574     loadData : function(o, append){
21575         var r = this.reader.readRecords(o);
21576         this.loadRecords(r, {add: append}, true);
21577     },
21578
21579     /**
21580      * Gets the number of cached records.
21581      * <p>
21582      * <em>If using paging, this may not be the total size of the dataset. If the data object
21583      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21584      * the data set size</em>
21585      */
21586     getCount : function(){
21587         return this.data.length || 0;
21588     },
21589
21590     /**
21591      * Gets the total number of records in the dataset as returned by the server.
21592      * <p>
21593      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21594      * the dataset size</em>
21595      */
21596     getTotalCount : function(){
21597         return this.totalLength || 0;
21598     },
21599
21600     /**
21601      * Returns the sort state of the Store as an object with two properties:
21602      * <pre><code>
21603  field {String} The name of the field by which the Records are sorted
21604  direction {String} The sort order, "ASC" or "DESC"
21605      * </code></pre>
21606      */
21607     getSortState : function(){
21608         return this.sortInfo;
21609     },
21610
21611     // private
21612     applySort : function(){
21613         if(this.sortInfo && !this.remoteSort){
21614             var s = this.sortInfo, f = s.field;
21615             var st = this.fields.get(f).sortType;
21616             var fn = function(r1, r2){
21617                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21618                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21619             };
21620             this.data.sort(s.direction, fn);
21621             if(this.snapshot && this.snapshot != this.data){
21622                 this.snapshot.sort(s.direction, fn);
21623             }
21624         }
21625     },
21626
21627     /**
21628      * Sets the default sort column and order to be used by the next load operation.
21629      * @param {String} fieldName The name of the field to sort by.
21630      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21631      */
21632     setDefaultSort : function(field, dir){
21633         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21634     },
21635
21636     /**
21637      * Sort the Records.
21638      * If remote sorting is used, the sort is performed on the server, and the cache is
21639      * reloaded. If local sorting is used, the cache is sorted internally.
21640      * @param {String} fieldName The name of the field to sort by.
21641      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21642      */
21643     sort : function(fieldName, dir){
21644         var f = this.fields.get(fieldName);
21645         if(!dir){
21646             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21647             
21648             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21649                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21650             }else{
21651                 dir = f.sortDir;
21652             }
21653         }
21654         this.sortToggle[f.name] = dir;
21655         this.sortInfo = {field: f.name, direction: dir};
21656         if(!this.remoteSort){
21657             this.applySort();
21658             this.fireEvent("datachanged", this);
21659         }else{
21660             this.load(this.lastOptions);
21661         }
21662     },
21663
21664     /**
21665      * Calls the specified function for each of the Records in the cache.
21666      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21667      * Returning <em>false</em> aborts and exits the iteration.
21668      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21669      */
21670     each : function(fn, scope){
21671         this.data.each(fn, scope);
21672     },
21673
21674     /**
21675      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21676      * (e.g., during paging).
21677      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21678      */
21679     getModifiedRecords : function(){
21680         return this.modified;
21681     },
21682
21683     // private
21684     createFilterFn : function(property, value, anyMatch){
21685         if(!value.exec){ // not a regex
21686             value = String(value);
21687             if(value.length == 0){
21688                 return false;
21689             }
21690             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21691         }
21692         return function(r){
21693             return value.test(r.data[property]);
21694         };
21695     },
21696
21697     /**
21698      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21699      * @param {String} property A field on your records
21700      * @param {Number} start The record index to start at (defaults to 0)
21701      * @param {Number} end The last record index to include (defaults to length - 1)
21702      * @return {Number} The sum
21703      */
21704     sum : function(property, start, end){
21705         var rs = this.data.items, v = 0;
21706         start = start || 0;
21707         end = (end || end === 0) ? end : rs.length-1;
21708
21709         for(var i = start; i <= end; i++){
21710             v += (rs[i].data[property] || 0);
21711         }
21712         return v;
21713     },
21714
21715     /**
21716      * Filter the records by a specified property.
21717      * @param {String} field A field on your records
21718      * @param {String/RegExp} value Either a string that the field
21719      * should start with or a RegExp to test against the field
21720      * @param {Boolean} anyMatch True to match any part not just the beginning
21721      */
21722     filter : function(property, value, anyMatch){
21723         var fn = this.createFilterFn(property, value, anyMatch);
21724         return fn ? this.filterBy(fn) : this.clearFilter();
21725     },
21726
21727     /**
21728      * Filter by a function. The specified function will be called with each
21729      * record in this data source. If the function returns true the record is included,
21730      * otherwise it is filtered.
21731      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21732      * @param {Object} scope (optional) The scope of the function (defaults to this)
21733      */
21734     filterBy : function(fn, scope){
21735         this.snapshot = this.snapshot || this.data;
21736         this.data = this.queryBy(fn, scope||this);
21737         this.fireEvent("datachanged", this);
21738     },
21739
21740     /**
21741      * Query the records by a specified property.
21742      * @param {String} field A field on your records
21743      * @param {String/RegExp} value Either a string that the field
21744      * should start with or a RegExp to test against the field
21745      * @param {Boolean} anyMatch True to match any part not just the beginning
21746      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21747      */
21748     query : function(property, value, anyMatch){
21749         var fn = this.createFilterFn(property, value, anyMatch);
21750         return fn ? this.queryBy(fn) : this.data.clone();
21751     },
21752
21753     /**
21754      * Query by a function. The specified function will be called with each
21755      * record in this data source. If the function returns true the record is included
21756      * in the results.
21757      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21758      * @param {Object} scope (optional) The scope of the function (defaults to this)
21759       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21760      **/
21761     queryBy : function(fn, scope){
21762         var data = this.snapshot || this.data;
21763         return data.filterBy(fn, scope||this);
21764     },
21765
21766     /**
21767      * Collects unique values for a particular dataIndex from this store.
21768      * @param {String} dataIndex The property to collect
21769      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21770      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21771      * @return {Array} An array of the unique values
21772      **/
21773     collect : function(dataIndex, allowNull, bypassFilter){
21774         var d = (bypassFilter === true && this.snapshot) ?
21775                 this.snapshot.items : this.data.items;
21776         var v, sv, r = [], l = {};
21777         for(var i = 0, len = d.length; i < len; i++){
21778             v = d[i].data[dataIndex];
21779             sv = String(v);
21780             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21781                 l[sv] = true;
21782                 r[r.length] = v;
21783             }
21784         }
21785         return r;
21786     },
21787
21788     /**
21789      * Revert to a view of the Record cache with no filtering applied.
21790      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21791      */
21792     clearFilter : function(suppressEvent){
21793         if(this.snapshot && this.snapshot != this.data){
21794             this.data = this.snapshot;
21795             delete this.snapshot;
21796             if(suppressEvent !== true){
21797                 this.fireEvent("datachanged", this);
21798             }
21799         }
21800     },
21801
21802     // private
21803     afterEdit : function(record){
21804         if(this.modified.indexOf(record) == -1){
21805             this.modified.push(record);
21806         }
21807         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21808     },
21809     
21810     // private
21811     afterReject : function(record){
21812         this.modified.remove(record);
21813         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21814     },
21815
21816     // private
21817     afterCommit : function(record){
21818         this.modified.remove(record);
21819         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21820     },
21821
21822     /**
21823      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21824      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21825      */
21826     commitChanges : function(){
21827         var m = this.modified.slice(0);
21828         this.modified = [];
21829         for(var i = 0, len = m.length; i < len; i++){
21830             m[i].commit();
21831         }
21832     },
21833
21834     /**
21835      * Cancel outstanding changes on all changed records.
21836      */
21837     rejectChanges : function(){
21838         var m = this.modified.slice(0);
21839         this.modified = [];
21840         for(var i = 0, len = m.length; i < len; i++){
21841             m[i].reject();
21842         }
21843     },
21844
21845     onMetaChange : function(meta, rtype, o){
21846         this.recordType = rtype;
21847         this.fields = rtype.prototype.fields;
21848         delete this.snapshot;
21849         this.sortInfo = meta.sortInfo || this.sortInfo;
21850         this.modified = [];
21851         this.fireEvent('metachange', this, this.reader.meta);
21852     },
21853     
21854     moveIndex : function(data, type)
21855     {
21856         var index = this.indexOf(data);
21857         
21858         var newIndex = index + type;
21859         
21860         this.remove(data);
21861         
21862         this.insert(newIndex, data);
21863         
21864     }
21865 });/*
21866  * Based on:
21867  * Ext JS Library 1.1.1
21868  * Copyright(c) 2006-2007, Ext JS, LLC.
21869  *
21870  * Originally Released Under LGPL - original licence link has changed is not relivant.
21871  *
21872  * Fork - LGPL
21873  * <script type="text/javascript">
21874  */
21875
21876 /**
21877  * @class Roo.data.SimpleStore
21878  * @extends Roo.data.Store
21879  * Small helper class to make creating Stores from Array data easier.
21880  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21881  * @cfg {Array} fields An array of field definition objects, or field name strings.
21882  * @cfg {Array} data The multi-dimensional array of data
21883  * @constructor
21884  * @param {Object} config
21885  */
21886 Roo.data.SimpleStore = function(config){
21887     Roo.data.SimpleStore.superclass.constructor.call(this, {
21888         isLocal : true,
21889         reader: new Roo.data.ArrayReader({
21890                 id: config.id
21891             },
21892             Roo.data.Record.create(config.fields)
21893         ),
21894         proxy : new Roo.data.MemoryProxy(config.data)
21895     });
21896     this.load();
21897 };
21898 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21899  * Based on:
21900  * Ext JS Library 1.1.1
21901  * Copyright(c) 2006-2007, Ext JS, LLC.
21902  *
21903  * Originally Released Under LGPL - original licence link has changed is not relivant.
21904  *
21905  * Fork - LGPL
21906  * <script type="text/javascript">
21907  */
21908
21909 /**
21910 /**
21911  * @extends Roo.data.Store
21912  * @class Roo.data.JsonStore
21913  * Small helper class to make creating Stores for JSON data easier. <br/>
21914 <pre><code>
21915 var store = new Roo.data.JsonStore({
21916     url: 'get-images.php',
21917     root: 'images',
21918     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21919 });
21920 </code></pre>
21921  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21922  * JsonReader and HttpProxy (unless inline data is provided).</b>
21923  * @cfg {Array} fields An array of field definition objects, or field name strings.
21924  * @constructor
21925  * @param {Object} config
21926  */
21927 Roo.data.JsonStore = function(c){
21928     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21929         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21930         reader: new Roo.data.JsonReader(c, c.fields)
21931     }));
21932 };
21933 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21934  * Based on:
21935  * Ext JS Library 1.1.1
21936  * Copyright(c) 2006-2007, Ext JS, LLC.
21937  *
21938  * Originally Released Under LGPL - original licence link has changed is not relivant.
21939  *
21940  * Fork - LGPL
21941  * <script type="text/javascript">
21942  */
21943
21944  
21945 Roo.data.Field = function(config){
21946     if(typeof config == "string"){
21947         config = {name: config};
21948     }
21949     Roo.apply(this, config);
21950     
21951     if(!this.type){
21952         this.type = "auto";
21953     }
21954     
21955     var st = Roo.data.SortTypes;
21956     // named sortTypes are supported, here we look them up
21957     if(typeof this.sortType == "string"){
21958         this.sortType = st[this.sortType];
21959     }
21960     
21961     // set default sortType for strings and dates
21962     if(!this.sortType){
21963         switch(this.type){
21964             case "string":
21965                 this.sortType = st.asUCString;
21966                 break;
21967             case "date":
21968                 this.sortType = st.asDate;
21969                 break;
21970             default:
21971                 this.sortType = st.none;
21972         }
21973     }
21974
21975     // define once
21976     var stripRe = /[\$,%]/g;
21977
21978     // prebuilt conversion function for this field, instead of
21979     // switching every time we're reading a value
21980     if(!this.convert){
21981         var cv, dateFormat = this.dateFormat;
21982         switch(this.type){
21983             case "":
21984             case "auto":
21985             case undefined:
21986                 cv = function(v){ return v; };
21987                 break;
21988             case "string":
21989                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21990                 break;
21991             case "int":
21992                 cv = function(v){
21993                     return v !== undefined && v !== null && v !== '' ?
21994                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21995                     };
21996                 break;
21997             case "float":
21998                 cv = function(v){
21999                     return v !== undefined && v !== null && v !== '' ?
22000                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22001                     };
22002                 break;
22003             case "bool":
22004             case "boolean":
22005                 cv = function(v){ return v === true || v === "true" || v == 1; };
22006                 break;
22007             case "date":
22008                 cv = function(v){
22009                     if(!v){
22010                         return '';
22011                     }
22012                     if(v instanceof Date){
22013                         return v;
22014                     }
22015                     if(dateFormat){
22016                         if(dateFormat == "timestamp"){
22017                             return new Date(v*1000);
22018                         }
22019                         return Date.parseDate(v, dateFormat);
22020                     }
22021                     var parsed = Date.parse(v);
22022                     return parsed ? new Date(parsed) : null;
22023                 };
22024              break;
22025             
22026         }
22027         this.convert = cv;
22028     }
22029 };
22030
22031 Roo.data.Field.prototype = {
22032     dateFormat: null,
22033     defaultValue: "",
22034     mapping: null,
22035     sortType : null,
22036     sortDir : "ASC"
22037 };/*
22038  * Based on:
22039  * Ext JS Library 1.1.1
22040  * Copyright(c) 2006-2007, Ext JS, LLC.
22041  *
22042  * Originally Released Under LGPL - original licence link has changed is not relivant.
22043  *
22044  * Fork - LGPL
22045  * <script type="text/javascript">
22046  */
22047  
22048 // Base class for reading structured data from a data source.  This class is intended to be
22049 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22050
22051 /**
22052  * @class Roo.data.DataReader
22053  * Base class for reading structured data from a data source.  This class is intended to be
22054  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22055  */
22056
22057 Roo.data.DataReader = function(meta, recordType){
22058     
22059     this.meta = meta;
22060     
22061     this.recordType = recordType instanceof Array ? 
22062         Roo.data.Record.create(recordType) : recordType;
22063 };
22064
22065 Roo.data.DataReader.prototype = {
22066      /**
22067      * Create an empty record
22068      * @param {Object} data (optional) - overlay some values
22069      * @return {Roo.data.Record} record created.
22070      */
22071     newRow :  function(d) {
22072         var da =  {};
22073         this.recordType.prototype.fields.each(function(c) {
22074             switch( c.type) {
22075                 case 'int' : da[c.name] = 0; break;
22076                 case 'date' : da[c.name] = new Date(); break;
22077                 case 'float' : da[c.name] = 0.0; break;
22078                 case 'boolean' : da[c.name] = false; break;
22079                 default : da[c.name] = ""; break;
22080             }
22081             
22082         });
22083         return new this.recordType(Roo.apply(da, d));
22084     }
22085     
22086 };/*
22087  * Based on:
22088  * Ext JS Library 1.1.1
22089  * Copyright(c) 2006-2007, Ext JS, LLC.
22090  *
22091  * Originally Released Under LGPL - original licence link has changed is not relivant.
22092  *
22093  * Fork - LGPL
22094  * <script type="text/javascript">
22095  */
22096
22097 /**
22098  * @class Roo.data.DataProxy
22099  * @extends Roo.data.Observable
22100  * This class is an abstract base class for implementations which provide retrieval of
22101  * unformatted data objects.<br>
22102  * <p>
22103  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22104  * (of the appropriate type which knows how to parse the data object) to provide a block of
22105  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22106  * <p>
22107  * Custom implementations must implement the load method as described in
22108  * {@link Roo.data.HttpProxy#load}.
22109  */
22110 Roo.data.DataProxy = function(){
22111     this.addEvents({
22112         /**
22113          * @event beforeload
22114          * Fires before a network request is made to retrieve a data object.
22115          * @param {Object} This DataProxy object.
22116          * @param {Object} params The params parameter to the load function.
22117          */
22118         beforeload : true,
22119         /**
22120          * @event load
22121          * Fires before the load method's callback is called.
22122          * @param {Object} This DataProxy object.
22123          * @param {Object} o The data object.
22124          * @param {Object} arg The callback argument object passed to the load function.
22125          */
22126         load : true,
22127         /**
22128          * @event loadexception
22129          * Fires if an Exception occurs during data retrieval.
22130          * @param {Object} This DataProxy object.
22131          * @param {Object} o The data object.
22132          * @param {Object} arg The callback argument object passed to the load function.
22133          * @param {Object} e The Exception.
22134          */
22135         loadexception : true
22136     });
22137     Roo.data.DataProxy.superclass.constructor.call(this);
22138 };
22139
22140 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22141
22142     /**
22143      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22144      */
22145 /*
22146  * Based on:
22147  * Ext JS Library 1.1.1
22148  * Copyright(c) 2006-2007, Ext JS, LLC.
22149  *
22150  * Originally Released Under LGPL - original licence link has changed is not relivant.
22151  *
22152  * Fork - LGPL
22153  * <script type="text/javascript">
22154  */
22155 /**
22156  * @class Roo.data.MemoryProxy
22157  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22158  * to the Reader when its load method is called.
22159  * @constructor
22160  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22161  */
22162 Roo.data.MemoryProxy = function(data){
22163     if (data.data) {
22164         data = data.data;
22165     }
22166     Roo.data.MemoryProxy.superclass.constructor.call(this);
22167     this.data = data;
22168 };
22169
22170 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22171     /**
22172      * Load data from the requested source (in this case an in-memory
22173      * data object passed to the constructor), read the data object into
22174      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22175      * process that block using the passed callback.
22176      * @param {Object} params This parameter is not used by the MemoryProxy class.
22177      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22178      * object into a block of Roo.data.Records.
22179      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22180      * The function must be passed <ul>
22181      * <li>The Record block object</li>
22182      * <li>The "arg" argument from the load function</li>
22183      * <li>A boolean success indicator</li>
22184      * </ul>
22185      * @param {Object} scope The scope in which to call the callback
22186      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22187      */
22188     load : function(params, reader, callback, scope, arg){
22189         params = params || {};
22190         var result;
22191         try {
22192             result = reader.readRecords(this.data);
22193         }catch(e){
22194             this.fireEvent("loadexception", this, arg, null, e);
22195             callback.call(scope, null, arg, false);
22196             return;
22197         }
22198         callback.call(scope, result, arg, true);
22199     },
22200     
22201     // private
22202     update : function(params, records){
22203         
22204     }
22205 });/*
22206  * Based on:
22207  * Ext JS Library 1.1.1
22208  * Copyright(c) 2006-2007, Ext JS, LLC.
22209  *
22210  * Originally Released Under LGPL - original licence link has changed is not relivant.
22211  *
22212  * Fork - LGPL
22213  * <script type="text/javascript">
22214  */
22215 /**
22216  * @class Roo.data.HttpProxy
22217  * @extends Roo.data.DataProxy
22218  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22219  * configured to reference a certain URL.<br><br>
22220  * <p>
22221  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22222  * from which the running page was served.<br><br>
22223  * <p>
22224  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22225  * <p>
22226  * Be aware that to enable the browser to parse an XML document, the server must set
22227  * the Content-Type header in the HTTP response to "text/xml".
22228  * @constructor
22229  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22230  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22231  * will be used to make the request.
22232  */
22233 Roo.data.HttpProxy = function(conn){
22234     Roo.data.HttpProxy.superclass.constructor.call(this);
22235     // is conn a conn config or a real conn?
22236     this.conn = conn;
22237     this.useAjax = !conn || !conn.events;
22238   
22239 };
22240
22241 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22242     // thse are take from connection...
22243     
22244     /**
22245      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22246      */
22247     /**
22248      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22249      * extra parameters to each request made by this object. (defaults to undefined)
22250      */
22251     /**
22252      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22253      *  to each request made by this object. (defaults to undefined)
22254      */
22255     /**
22256      * @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)
22257      */
22258     /**
22259      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22260      */
22261      /**
22262      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22263      * @type Boolean
22264      */
22265   
22266
22267     /**
22268      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22269      * @type Boolean
22270      */
22271     /**
22272      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22273      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22274      * a finer-grained basis than the DataProxy events.
22275      */
22276     getConnection : function(){
22277         return this.useAjax ? Roo.Ajax : this.conn;
22278     },
22279
22280     /**
22281      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22282      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22283      * process that block using the passed callback.
22284      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22285      * for the request to the remote server.
22286      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22287      * object into a block of Roo.data.Records.
22288      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22289      * The function must be passed <ul>
22290      * <li>The Record block object</li>
22291      * <li>The "arg" argument from the load function</li>
22292      * <li>A boolean success indicator</li>
22293      * </ul>
22294      * @param {Object} scope The scope in which to call the callback
22295      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22296      */
22297     load : function(params, reader, callback, scope, arg){
22298         if(this.fireEvent("beforeload", this, params) !== false){
22299             var  o = {
22300                 params : params || {},
22301                 request: {
22302                     callback : callback,
22303                     scope : scope,
22304                     arg : arg
22305                 },
22306                 reader: reader,
22307                 callback : this.loadResponse,
22308                 scope: this
22309             };
22310             if(this.useAjax){
22311                 Roo.applyIf(o, this.conn);
22312                 if(this.activeRequest){
22313                     Roo.Ajax.abort(this.activeRequest);
22314                 }
22315                 this.activeRequest = Roo.Ajax.request(o);
22316             }else{
22317                 this.conn.request(o);
22318             }
22319         }else{
22320             callback.call(scope||this, null, arg, false);
22321         }
22322     },
22323
22324     // private
22325     loadResponse : function(o, success, response){
22326         delete this.activeRequest;
22327         if(!success){
22328             this.fireEvent("loadexception", this, o, response);
22329             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22330             return;
22331         }
22332         var result;
22333         try {
22334             result = o.reader.read(response);
22335         }catch(e){
22336             this.fireEvent("loadexception", this, o, response, e);
22337             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22338             return;
22339         }
22340         
22341         this.fireEvent("load", this, o, o.request.arg);
22342         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22343     },
22344
22345     // private
22346     update : function(dataSet){
22347
22348     },
22349
22350     // private
22351     updateResponse : function(dataSet){
22352
22353     }
22354 });/*
22355  * Based on:
22356  * Ext JS Library 1.1.1
22357  * Copyright(c) 2006-2007, Ext JS, LLC.
22358  *
22359  * Originally Released Under LGPL - original licence link has changed is not relivant.
22360  *
22361  * Fork - LGPL
22362  * <script type="text/javascript">
22363  */
22364
22365 /**
22366  * @class Roo.data.ScriptTagProxy
22367  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22368  * other than the originating domain of the running page.<br><br>
22369  * <p>
22370  * <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
22371  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22372  * <p>
22373  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22374  * source code that is used as the source inside a &lt;script> tag.<br><br>
22375  * <p>
22376  * In order for the browser to process the returned data, the server must wrap the data object
22377  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22378  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22379  * depending on whether the callback name was passed:
22380  * <p>
22381  * <pre><code>
22382 boolean scriptTag = false;
22383 String cb = request.getParameter("callback");
22384 if (cb != null) {
22385     scriptTag = true;
22386     response.setContentType("text/javascript");
22387 } else {
22388     response.setContentType("application/x-json");
22389 }
22390 Writer out = response.getWriter();
22391 if (scriptTag) {
22392     out.write(cb + "(");
22393 }
22394 out.print(dataBlock.toJsonString());
22395 if (scriptTag) {
22396     out.write(");");
22397 }
22398 </pre></code>
22399  *
22400  * @constructor
22401  * @param {Object} config A configuration object.
22402  */
22403 Roo.data.ScriptTagProxy = function(config){
22404     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22405     Roo.apply(this, config);
22406     this.head = document.getElementsByTagName("head")[0];
22407 };
22408
22409 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22410
22411 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22412     /**
22413      * @cfg {String} url The URL from which to request the data object.
22414      */
22415     /**
22416      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22417      */
22418     timeout : 30000,
22419     /**
22420      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22421      * the server the name of the callback function set up by the load call to process the returned data object.
22422      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22423      * javascript output which calls this named function passing the data object as its only parameter.
22424      */
22425     callbackParam : "callback",
22426     /**
22427      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22428      * name to the request.
22429      */
22430     nocache : true,
22431
22432     /**
22433      * Load data from the configured URL, read the data object into
22434      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22435      * process that block using the passed callback.
22436      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22437      * for the request to the remote server.
22438      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22439      * object into a block of Roo.data.Records.
22440      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22441      * The function must be passed <ul>
22442      * <li>The Record block object</li>
22443      * <li>The "arg" argument from the load function</li>
22444      * <li>A boolean success indicator</li>
22445      * </ul>
22446      * @param {Object} scope The scope in which to call the callback
22447      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22448      */
22449     load : function(params, reader, callback, scope, arg){
22450         if(this.fireEvent("beforeload", this, params) !== false){
22451
22452             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22453
22454             var url = this.url;
22455             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22456             if(this.nocache){
22457                 url += "&_dc=" + (new Date().getTime());
22458             }
22459             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22460             var trans = {
22461                 id : transId,
22462                 cb : "stcCallback"+transId,
22463                 scriptId : "stcScript"+transId,
22464                 params : params,
22465                 arg : arg,
22466                 url : url,
22467                 callback : callback,
22468                 scope : scope,
22469                 reader : reader
22470             };
22471             var conn = this;
22472
22473             window[trans.cb] = function(o){
22474                 conn.handleResponse(o, trans);
22475             };
22476
22477             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22478
22479             if(this.autoAbort !== false){
22480                 this.abort();
22481             }
22482
22483             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22484
22485             var script = document.createElement("script");
22486             script.setAttribute("src", url);
22487             script.setAttribute("type", "text/javascript");
22488             script.setAttribute("id", trans.scriptId);
22489             this.head.appendChild(script);
22490
22491             this.trans = trans;
22492         }else{
22493             callback.call(scope||this, null, arg, false);
22494         }
22495     },
22496
22497     // private
22498     isLoading : function(){
22499         return this.trans ? true : false;
22500     },
22501
22502     /**
22503      * Abort the current server request.
22504      */
22505     abort : function(){
22506         if(this.isLoading()){
22507             this.destroyTrans(this.trans);
22508         }
22509     },
22510
22511     // private
22512     destroyTrans : function(trans, isLoaded){
22513         this.head.removeChild(document.getElementById(trans.scriptId));
22514         clearTimeout(trans.timeoutId);
22515         if(isLoaded){
22516             window[trans.cb] = undefined;
22517             try{
22518                 delete window[trans.cb];
22519             }catch(e){}
22520         }else{
22521             // if hasn't been loaded, wait for load to remove it to prevent script error
22522             window[trans.cb] = function(){
22523                 window[trans.cb] = undefined;
22524                 try{
22525                     delete window[trans.cb];
22526                 }catch(e){}
22527             };
22528         }
22529     },
22530
22531     // private
22532     handleResponse : function(o, trans){
22533         this.trans = false;
22534         this.destroyTrans(trans, true);
22535         var result;
22536         try {
22537             result = trans.reader.readRecords(o);
22538         }catch(e){
22539             this.fireEvent("loadexception", this, o, trans.arg, e);
22540             trans.callback.call(trans.scope||window, null, trans.arg, false);
22541             return;
22542         }
22543         this.fireEvent("load", this, o, trans.arg);
22544         trans.callback.call(trans.scope||window, result, trans.arg, true);
22545     },
22546
22547     // private
22548     handleFailure : function(trans){
22549         this.trans = false;
22550         this.destroyTrans(trans, false);
22551         this.fireEvent("loadexception", this, null, trans.arg);
22552         trans.callback.call(trans.scope||window, null, trans.arg, false);
22553     }
22554 });/*
22555  * Based on:
22556  * Ext JS Library 1.1.1
22557  * Copyright(c) 2006-2007, Ext JS, LLC.
22558  *
22559  * Originally Released Under LGPL - original licence link has changed is not relivant.
22560  *
22561  * Fork - LGPL
22562  * <script type="text/javascript">
22563  */
22564
22565 /**
22566  * @class Roo.data.JsonReader
22567  * @extends Roo.data.DataReader
22568  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22569  * based on mappings in a provided Roo.data.Record constructor.
22570  * 
22571  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22572  * in the reply previously. 
22573  * 
22574  * <p>
22575  * Example code:
22576  * <pre><code>
22577 var RecordDef = Roo.data.Record.create([
22578     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22579     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22580 ]);
22581 var myReader = new Roo.data.JsonReader({
22582     totalProperty: "results",    // The property which contains the total dataset size (optional)
22583     root: "rows",                // The property which contains an Array of row objects
22584     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22585 }, RecordDef);
22586 </code></pre>
22587  * <p>
22588  * This would consume a JSON file like this:
22589  * <pre><code>
22590 { 'results': 2, 'rows': [
22591     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22592     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22593 }
22594 </code></pre>
22595  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22596  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22597  * paged from the remote server.
22598  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22599  * @cfg {String} root name of the property which contains the Array of row objects.
22600  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22601  * @constructor
22602  * Create a new JsonReader
22603  * @param {Object} meta Metadata configuration options
22604  * @param {Object} recordType Either an Array of field definition objects,
22605  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22606  */
22607 Roo.data.JsonReader = function(meta, recordType){
22608     
22609     meta = meta || {};
22610     // set some defaults:
22611     Roo.applyIf(meta, {
22612         totalProperty: 'total',
22613         successProperty : 'success',
22614         root : 'data',
22615         id : 'id'
22616     });
22617     
22618     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22619 };
22620 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22621     
22622     /**
22623      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22624      * Used by Store query builder to append _requestMeta to params.
22625      * 
22626      */
22627     metaFromRemote : false,
22628     /**
22629      * This method is only used by a DataProxy which has retrieved data from a remote server.
22630      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22631      * @return {Object} data A data block which is used by an Roo.data.Store object as
22632      * a cache of Roo.data.Records.
22633      */
22634     read : function(response){
22635         var json = response.responseText;
22636        
22637         var o = /* eval:var:o */ eval("("+json+")");
22638         if(!o) {
22639             throw {message: "JsonReader.read: Json object not found"};
22640         }
22641         
22642         if(o.metaData){
22643             
22644             delete this.ef;
22645             this.metaFromRemote = true;
22646             this.meta = o.metaData;
22647             this.recordType = Roo.data.Record.create(o.metaData.fields);
22648             this.onMetaChange(this.meta, this.recordType, o);
22649         }
22650         return this.readRecords(o);
22651     },
22652
22653     // private function a store will implement
22654     onMetaChange : function(meta, recordType, o){
22655
22656     },
22657
22658     /**
22659          * @ignore
22660          */
22661     simpleAccess: function(obj, subsc) {
22662         return obj[subsc];
22663     },
22664
22665         /**
22666          * @ignore
22667          */
22668     getJsonAccessor: function(){
22669         var re = /[\[\.]/;
22670         return function(expr) {
22671             try {
22672                 return(re.test(expr))
22673                     ? new Function("obj", "return obj." + expr)
22674                     : function(obj){
22675                         return obj[expr];
22676                     };
22677             } catch(e){}
22678             return Roo.emptyFn;
22679         };
22680     }(),
22681
22682     /**
22683      * Create a data block containing Roo.data.Records from an XML document.
22684      * @param {Object} o An object which contains an Array of row objects in the property specified
22685      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22686      * which contains the total size of the dataset.
22687      * @return {Object} data A data block which is used by an Roo.data.Store object as
22688      * a cache of Roo.data.Records.
22689      */
22690     readRecords : function(o){
22691         /**
22692          * After any data loads, the raw JSON data is available for further custom processing.
22693          * @type Object
22694          */
22695         this.o = o;
22696         var s = this.meta, Record = this.recordType,
22697             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22698
22699 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22700         if (!this.ef) {
22701             if(s.totalProperty) {
22702                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22703                 }
22704                 if(s.successProperty) {
22705                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22706                 }
22707                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22708                 if (s.id) {
22709                         var g = this.getJsonAccessor(s.id);
22710                         this.getId = function(rec) {
22711                                 var r = g(rec);  
22712                                 return (r === undefined || r === "") ? null : r;
22713                         };
22714                 } else {
22715                         this.getId = function(){return null;};
22716                 }
22717             this.ef = [];
22718             for(var jj = 0; jj < fl; jj++){
22719                 f = fi[jj];
22720                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22721                 this.ef[jj] = this.getJsonAccessor(map);
22722             }
22723         }
22724
22725         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22726         if(s.totalProperty){
22727             var vt = parseInt(this.getTotal(o), 10);
22728             if(!isNaN(vt)){
22729                 totalRecords = vt;
22730             }
22731         }
22732         if(s.successProperty){
22733             var vs = this.getSuccess(o);
22734             if(vs === false || vs === 'false'){
22735                 success = false;
22736             }
22737         }
22738         var records = [];
22739         for(var i = 0; i < c; i++){
22740                 var n = root[i];
22741             var values = {};
22742             var id = this.getId(n);
22743             for(var j = 0; j < fl; j++){
22744                 f = fi[j];
22745             var v = this.ef[j](n);
22746             if (!f.convert) {
22747                 Roo.log('missing convert for ' + f.name);
22748                 Roo.log(f);
22749                 continue;
22750             }
22751             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22752             }
22753             var record = new Record(values, id);
22754             record.json = n;
22755             records[i] = record;
22756         }
22757         return {
22758             raw : o,
22759             success : success,
22760             records : records,
22761             totalRecords : totalRecords
22762         };
22763     }
22764 });/*
22765  * Based on:
22766  * Ext JS Library 1.1.1
22767  * Copyright(c) 2006-2007, Ext JS, LLC.
22768  *
22769  * Originally Released Under LGPL - original licence link has changed is not relivant.
22770  *
22771  * Fork - LGPL
22772  * <script type="text/javascript">
22773  */
22774
22775 /**
22776  * @class Roo.data.XmlReader
22777  * @extends Roo.data.DataReader
22778  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22779  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22780  * <p>
22781  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22782  * header in the HTTP response must be set to "text/xml".</em>
22783  * <p>
22784  * Example code:
22785  * <pre><code>
22786 var RecordDef = Roo.data.Record.create([
22787    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22788    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22789 ]);
22790 var myReader = new Roo.data.XmlReader({
22791    totalRecords: "results", // The element which contains the total dataset size (optional)
22792    record: "row",           // The repeated element which contains row information
22793    id: "id"                 // The element within the row that provides an ID for the record (optional)
22794 }, RecordDef);
22795 </code></pre>
22796  * <p>
22797  * This would consume an XML file like this:
22798  * <pre><code>
22799 &lt;?xml?>
22800 &lt;dataset>
22801  &lt;results>2&lt;/results>
22802  &lt;row>
22803    &lt;id>1&lt;/id>
22804    &lt;name>Bill&lt;/name>
22805    &lt;occupation>Gardener&lt;/occupation>
22806  &lt;/row>
22807  &lt;row>
22808    &lt;id>2&lt;/id>
22809    &lt;name>Ben&lt;/name>
22810    &lt;occupation>Horticulturalist&lt;/occupation>
22811  &lt;/row>
22812 &lt;/dataset>
22813 </code></pre>
22814  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22815  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22816  * paged from the remote server.
22817  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22818  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22819  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22820  * a record identifier value.
22821  * @constructor
22822  * Create a new XmlReader
22823  * @param {Object} meta Metadata configuration options
22824  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22825  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22826  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22827  */
22828 Roo.data.XmlReader = function(meta, recordType){
22829     meta = meta || {};
22830     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22831 };
22832 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22833     /**
22834      * This method is only used by a DataProxy which has retrieved data from a remote server.
22835          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22836          * to contain a method called 'responseXML' that returns an XML document object.
22837      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22838      * a cache of Roo.data.Records.
22839      */
22840     read : function(response){
22841         var doc = response.responseXML;
22842         if(!doc) {
22843             throw {message: "XmlReader.read: XML Document not available"};
22844         }
22845         return this.readRecords(doc);
22846     },
22847
22848     /**
22849      * Create a data block containing Roo.data.Records from an XML document.
22850          * @param {Object} doc A parsed XML document.
22851      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22852      * a cache of Roo.data.Records.
22853      */
22854     readRecords : function(doc){
22855         /**
22856          * After any data loads/reads, the raw XML Document is available for further custom processing.
22857          * @type XMLDocument
22858          */
22859         this.xmlData = doc;
22860         var root = doc.documentElement || doc;
22861         var q = Roo.DomQuery;
22862         var recordType = this.recordType, fields = recordType.prototype.fields;
22863         var sid = this.meta.id;
22864         var totalRecords = 0, success = true;
22865         if(this.meta.totalRecords){
22866             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22867         }
22868         
22869         if(this.meta.success){
22870             var sv = q.selectValue(this.meta.success, root, true);
22871             success = sv !== false && sv !== 'false';
22872         }
22873         var records = [];
22874         var ns = q.select(this.meta.record, root);
22875         for(var i = 0, len = ns.length; i < len; i++) {
22876                 var n = ns[i];
22877                 var values = {};
22878                 var id = sid ? q.selectValue(sid, n) : undefined;
22879                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22880                     var f = fields.items[j];
22881                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22882                     v = f.convert(v);
22883                     values[f.name] = v;
22884                 }
22885                 var record = new recordType(values, id);
22886                 record.node = n;
22887                 records[records.length] = record;
22888             }
22889
22890             return {
22891                 success : success,
22892                 records : records,
22893                 totalRecords : totalRecords || records.length
22894             };
22895     }
22896 });/*
22897  * Based on:
22898  * Ext JS Library 1.1.1
22899  * Copyright(c) 2006-2007, Ext JS, LLC.
22900  *
22901  * Originally Released Under LGPL - original licence link has changed is not relivant.
22902  *
22903  * Fork - LGPL
22904  * <script type="text/javascript">
22905  */
22906
22907 /**
22908  * @class Roo.data.ArrayReader
22909  * @extends Roo.data.DataReader
22910  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22911  * Each element of that Array represents a row of data fields. The
22912  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22913  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22914  * <p>
22915  * Example code:.
22916  * <pre><code>
22917 var RecordDef = Roo.data.Record.create([
22918     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22919     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22920 ]);
22921 var myReader = new Roo.data.ArrayReader({
22922     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22923 }, RecordDef);
22924 </code></pre>
22925  * <p>
22926  * This would consume an Array like this:
22927  * <pre><code>
22928 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22929   </code></pre>
22930  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22931  * @constructor
22932  * Create a new JsonReader
22933  * @param {Object} meta Metadata configuration options.
22934  * @param {Object} recordType Either an Array of field definition objects
22935  * as specified to {@link Roo.data.Record#create},
22936  * or an {@link Roo.data.Record} object
22937  * created using {@link Roo.data.Record#create}.
22938  */
22939 Roo.data.ArrayReader = function(meta, recordType){
22940     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22941 };
22942
22943 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22944     /**
22945      * Create a data block containing Roo.data.Records from an XML document.
22946      * @param {Object} o An Array of row objects which represents the dataset.
22947      * @return {Object} data A data block which is used by an Roo.data.Store object as
22948      * a cache of Roo.data.Records.
22949      */
22950     readRecords : function(o){
22951         var sid = this.meta ? this.meta.id : null;
22952         var recordType = this.recordType, fields = recordType.prototype.fields;
22953         var records = [];
22954         var root = o;
22955             for(var i = 0; i < root.length; i++){
22956                     var n = root[i];
22957                 var values = {};
22958                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22959                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22960                 var f = fields.items[j];
22961                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22962                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22963                 v = f.convert(v);
22964                 values[f.name] = v;
22965             }
22966                 var record = new recordType(values, id);
22967                 record.json = n;
22968                 records[records.length] = record;
22969             }
22970             return {
22971                 records : records,
22972                 totalRecords : records.length
22973             };
22974     }
22975 });/*
22976  * Based on:
22977  * Ext JS Library 1.1.1
22978  * Copyright(c) 2006-2007, Ext JS, LLC.
22979  *
22980  * Originally Released Under LGPL - original licence link has changed is not relivant.
22981  *
22982  * Fork - LGPL
22983  * <script type="text/javascript">
22984  */
22985
22986
22987 /**
22988  * @class Roo.data.Tree
22989  * @extends Roo.util.Observable
22990  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22991  * in the tree have most standard DOM functionality.
22992  * @constructor
22993  * @param {Node} root (optional) The root node
22994  */
22995 Roo.data.Tree = function(root){
22996    this.nodeHash = {};
22997    /**
22998     * The root node for this tree
22999     * @type Node
23000     */
23001    this.root = null;
23002    if(root){
23003        this.setRootNode(root);
23004    }
23005    this.addEvents({
23006        /**
23007         * @event append
23008         * Fires when a new child node is appended to a node in this tree.
23009         * @param {Tree} tree The owner tree
23010         * @param {Node} parent The parent node
23011         * @param {Node} node The newly appended node
23012         * @param {Number} index The index of the newly appended node
23013         */
23014        "append" : true,
23015        /**
23016         * @event remove
23017         * Fires when a child node is removed from a node in this tree.
23018         * @param {Tree} tree The owner tree
23019         * @param {Node} parent The parent node
23020         * @param {Node} node The child node removed
23021         */
23022        "remove" : true,
23023        /**
23024         * @event move
23025         * Fires when a node is moved to a new location in the tree
23026         * @param {Tree} tree The owner tree
23027         * @param {Node} node The node moved
23028         * @param {Node} oldParent The old parent of this node
23029         * @param {Node} newParent The new parent of this node
23030         * @param {Number} index The index it was moved to
23031         */
23032        "move" : true,
23033        /**
23034         * @event insert
23035         * Fires when a new child node is inserted in a node in this tree.
23036         * @param {Tree} tree The owner tree
23037         * @param {Node} parent The parent node
23038         * @param {Node} node The child node inserted
23039         * @param {Node} refNode The child node the node was inserted before
23040         */
23041        "insert" : true,
23042        /**
23043         * @event beforeappend
23044         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23045         * @param {Tree} tree The owner tree
23046         * @param {Node} parent The parent node
23047         * @param {Node} node The child node to be appended
23048         */
23049        "beforeappend" : true,
23050        /**
23051         * @event beforeremove
23052         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23053         * @param {Tree} tree The owner tree
23054         * @param {Node} parent The parent node
23055         * @param {Node} node The child node to be removed
23056         */
23057        "beforeremove" : true,
23058        /**
23059         * @event beforemove
23060         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23061         * @param {Tree} tree The owner tree
23062         * @param {Node} node The node being moved
23063         * @param {Node} oldParent The parent of the node
23064         * @param {Node} newParent The new parent the node is moving to
23065         * @param {Number} index The index it is being moved to
23066         */
23067        "beforemove" : true,
23068        /**
23069         * @event beforeinsert
23070         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23071         * @param {Tree} tree The owner tree
23072         * @param {Node} parent The parent node
23073         * @param {Node} node The child node to be inserted
23074         * @param {Node} refNode The child node the node is being inserted before
23075         */
23076        "beforeinsert" : true
23077    });
23078
23079     Roo.data.Tree.superclass.constructor.call(this);
23080 };
23081
23082 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23083     pathSeparator: "/",
23084
23085     proxyNodeEvent : function(){
23086         return this.fireEvent.apply(this, arguments);
23087     },
23088
23089     /**
23090      * Returns the root node for this tree.
23091      * @return {Node}
23092      */
23093     getRootNode : function(){
23094         return this.root;
23095     },
23096
23097     /**
23098      * Sets the root node for this tree.
23099      * @param {Node} node
23100      * @return {Node}
23101      */
23102     setRootNode : function(node){
23103         this.root = node;
23104         node.ownerTree = this;
23105         node.isRoot = true;
23106         this.registerNode(node);
23107         return node;
23108     },
23109
23110     /**
23111      * Gets a node in this tree by its id.
23112      * @param {String} id
23113      * @return {Node}
23114      */
23115     getNodeById : function(id){
23116         return this.nodeHash[id];
23117     },
23118
23119     registerNode : function(node){
23120         this.nodeHash[node.id] = node;
23121     },
23122
23123     unregisterNode : function(node){
23124         delete this.nodeHash[node.id];
23125     },
23126
23127     toString : function(){
23128         return "[Tree"+(this.id?" "+this.id:"")+"]";
23129     }
23130 });
23131
23132 /**
23133  * @class Roo.data.Node
23134  * @extends Roo.util.Observable
23135  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23136  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23137  * @constructor
23138  * @param {Object} attributes The attributes/config for the node
23139  */
23140 Roo.data.Node = function(attributes){
23141     /**
23142      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23143      * @type {Object}
23144      */
23145     this.attributes = attributes || {};
23146     this.leaf = this.attributes.leaf;
23147     /**
23148      * The node id. @type String
23149      */
23150     this.id = this.attributes.id;
23151     if(!this.id){
23152         this.id = Roo.id(null, "ynode-");
23153         this.attributes.id = this.id;
23154     }
23155      
23156     
23157     /**
23158      * All child nodes of this node. @type Array
23159      */
23160     this.childNodes = [];
23161     if(!this.childNodes.indexOf){ // indexOf is a must
23162         this.childNodes.indexOf = function(o){
23163             for(var i = 0, len = this.length; i < len; i++){
23164                 if(this[i] == o) {
23165                     return i;
23166                 }
23167             }
23168             return -1;
23169         };
23170     }
23171     /**
23172      * The parent node for this node. @type Node
23173      */
23174     this.parentNode = null;
23175     /**
23176      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23177      */
23178     this.firstChild = null;
23179     /**
23180      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23181      */
23182     this.lastChild = null;
23183     /**
23184      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23185      */
23186     this.previousSibling = null;
23187     /**
23188      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23189      */
23190     this.nextSibling = null;
23191
23192     this.addEvents({
23193        /**
23194         * @event append
23195         * Fires when a new child node is appended
23196         * @param {Tree} tree The owner tree
23197         * @param {Node} this This node
23198         * @param {Node} node The newly appended node
23199         * @param {Number} index The index of the newly appended node
23200         */
23201        "append" : true,
23202        /**
23203         * @event remove
23204         * Fires when a child node is removed
23205         * @param {Tree} tree The owner tree
23206         * @param {Node} this This node
23207         * @param {Node} node The removed node
23208         */
23209        "remove" : true,
23210        /**
23211         * @event move
23212         * Fires when this node is moved to a new location in the tree
23213         * @param {Tree} tree The owner tree
23214         * @param {Node} this This node
23215         * @param {Node} oldParent The old parent of this node
23216         * @param {Node} newParent The new parent of this node
23217         * @param {Number} index The index it was moved to
23218         */
23219        "move" : true,
23220        /**
23221         * @event insert
23222         * Fires when a new child node is inserted.
23223         * @param {Tree} tree The owner tree
23224         * @param {Node} this This node
23225         * @param {Node} node The child node inserted
23226         * @param {Node} refNode The child node the node was inserted before
23227         */
23228        "insert" : true,
23229        /**
23230         * @event beforeappend
23231         * Fires before a new child is appended, return false to cancel the append.
23232         * @param {Tree} tree The owner tree
23233         * @param {Node} this This node
23234         * @param {Node} node The child node to be appended
23235         */
23236        "beforeappend" : true,
23237        /**
23238         * @event beforeremove
23239         * Fires before a child is removed, return false to cancel the remove.
23240         * @param {Tree} tree The owner tree
23241         * @param {Node} this This node
23242         * @param {Node} node The child node to be removed
23243         */
23244        "beforeremove" : true,
23245        /**
23246         * @event beforemove
23247         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23248         * @param {Tree} tree The owner tree
23249         * @param {Node} this This node
23250         * @param {Node} oldParent The parent of this node
23251         * @param {Node} newParent The new parent this node is moving to
23252         * @param {Number} index The index it is being moved to
23253         */
23254        "beforemove" : true,
23255        /**
23256         * @event beforeinsert
23257         * Fires before a new child is inserted, return false to cancel the insert.
23258         * @param {Tree} tree The owner tree
23259         * @param {Node} this This node
23260         * @param {Node} node The child node to be inserted
23261         * @param {Node} refNode The child node the node is being inserted before
23262         */
23263        "beforeinsert" : true
23264    });
23265     this.listeners = this.attributes.listeners;
23266     Roo.data.Node.superclass.constructor.call(this);
23267 };
23268
23269 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23270     fireEvent : function(evtName){
23271         // first do standard event for this node
23272         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23273             return false;
23274         }
23275         // then bubble it up to the tree if the event wasn't cancelled
23276         var ot = this.getOwnerTree();
23277         if(ot){
23278             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23279                 return false;
23280             }
23281         }
23282         return true;
23283     },
23284
23285     /**
23286      * Returns true if this node is a leaf
23287      * @return {Boolean}
23288      */
23289     isLeaf : function(){
23290         return this.leaf === true;
23291     },
23292
23293     // private
23294     setFirstChild : function(node){
23295         this.firstChild = node;
23296     },
23297
23298     //private
23299     setLastChild : function(node){
23300         this.lastChild = node;
23301     },
23302
23303
23304     /**
23305      * Returns true if this node is the last child of its parent
23306      * @return {Boolean}
23307      */
23308     isLast : function(){
23309        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23310     },
23311
23312     /**
23313      * Returns true if this node is the first child of its parent
23314      * @return {Boolean}
23315      */
23316     isFirst : function(){
23317        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23318     },
23319
23320     hasChildNodes : function(){
23321         return !this.isLeaf() && this.childNodes.length > 0;
23322     },
23323
23324     /**
23325      * Insert node(s) as the last child node of this node.
23326      * @param {Node/Array} node The node or Array of nodes to append
23327      * @return {Node} The appended node if single append, or null if an array was passed
23328      */
23329     appendChild : function(node){
23330         var multi = false;
23331         if(node instanceof Array){
23332             multi = node;
23333         }else if(arguments.length > 1){
23334             multi = arguments;
23335         }
23336         // if passed an array or multiple args do them one by one
23337         if(multi){
23338             for(var i = 0, len = multi.length; i < len; i++) {
23339                 this.appendChild(multi[i]);
23340             }
23341         }else{
23342             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23343                 return false;
23344             }
23345             var index = this.childNodes.length;
23346             var oldParent = node.parentNode;
23347             // it's a move, make sure we move it cleanly
23348             if(oldParent){
23349                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23350                     return false;
23351                 }
23352                 oldParent.removeChild(node);
23353             }
23354             index = this.childNodes.length;
23355             if(index == 0){
23356                 this.setFirstChild(node);
23357             }
23358             this.childNodes.push(node);
23359             node.parentNode = this;
23360             var ps = this.childNodes[index-1];
23361             if(ps){
23362                 node.previousSibling = ps;
23363                 ps.nextSibling = node;
23364             }else{
23365                 node.previousSibling = null;
23366             }
23367             node.nextSibling = null;
23368             this.setLastChild(node);
23369             node.setOwnerTree(this.getOwnerTree());
23370             this.fireEvent("append", this.ownerTree, this, node, index);
23371             if(oldParent){
23372                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23373             }
23374             return node;
23375         }
23376     },
23377
23378     /**
23379      * Removes a child node from this node.
23380      * @param {Node} node The node to remove
23381      * @return {Node} The removed node
23382      */
23383     removeChild : function(node){
23384         var index = this.childNodes.indexOf(node);
23385         if(index == -1){
23386             return false;
23387         }
23388         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23389             return false;
23390         }
23391
23392         // remove it from childNodes collection
23393         this.childNodes.splice(index, 1);
23394
23395         // update siblings
23396         if(node.previousSibling){
23397             node.previousSibling.nextSibling = node.nextSibling;
23398         }
23399         if(node.nextSibling){
23400             node.nextSibling.previousSibling = node.previousSibling;
23401         }
23402
23403         // update child refs
23404         if(this.firstChild == node){
23405             this.setFirstChild(node.nextSibling);
23406         }
23407         if(this.lastChild == node){
23408             this.setLastChild(node.previousSibling);
23409         }
23410
23411         node.setOwnerTree(null);
23412         // clear any references from the node
23413         node.parentNode = null;
23414         node.previousSibling = null;
23415         node.nextSibling = null;
23416         this.fireEvent("remove", this.ownerTree, this, node);
23417         return node;
23418     },
23419
23420     /**
23421      * Inserts the first node before the second node in this nodes childNodes collection.
23422      * @param {Node} node The node to insert
23423      * @param {Node} refNode The node to insert before (if null the node is appended)
23424      * @return {Node} The inserted node
23425      */
23426     insertBefore : function(node, refNode){
23427         if(!refNode){ // like standard Dom, refNode can be null for append
23428             return this.appendChild(node);
23429         }
23430         // nothing to do
23431         if(node == refNode){
23432             return false;
23433         }
23434
23435         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23436             return false;
23437         }
23438         var index = this.childNodes.indexOf(refNode);
23439         var oldParent = node.parentNode;
23440         var refIndex = index;
23441
23442         // when moving internally, indexes will change after remove
23443         if(oldParent == this && this.childNodes.indexOf(node) < index){
23444             refIndex--;
23445         }
23446
23447         // it's a move, make sure we move it cleanly
23448         if(oldParent){
23449             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23450                 return false;
23451             }
23452             oldParent.removeChild(node);
23453         }
23454         if(refIndex == 0){
23455             this.setFirstChild(node);
23456         }
23457         this.childNodes.splice(refIndex, 0, node);
23458         node.parentNode = this;
23459         var ps = this.childNodes[refIndex-1];
23460         if(ps){
23461             node.previousSibling = ps;
23462             ps.nextSibling = node;
23463         }else{
23464             node.previousSibling = null;
23465         }
23466         node.nextSibling = refNode;
23467         refNode.previousSibling = node;
23468         node.setOwnerTree(this.getOwnerTree());
23469         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23470         if(oldParent){
23471             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23472         }
23473         return node;
23474     },
23475
23476     /**
23477      * Returns the child node at the specified index.
23478      * @param {Number} index
23479      * @return {Node}
23480      */
23481     item : function(index){
23482         return this.childNodes[index];
23483     },
23484
23485     /**
23486      * Replaces one child node in this node with another.
23487      * @param {Node} newChild The replacement node
23488      * @param {Node} oldChild The node to replace
23489      * @return {Node} The replaced node
23490      */
23491     replaceChild : function(newChild, oldChild){
23492         this.insertBefore(newChild, oldChild);
23493         this.removeChild(oldChild);
23494         return oldChild;
23495     },
23496
23497     /**
23498      * Returns the index of a child node
23499      * @param {Node} node
23500      * @return {Number} The index of the node or -1 if it was not found
23501      */
23502     indexOf : function(child){
23503         return this.childNodes.indexOf(child);
23504     },
23505
23506     /**
23507      * Returns the tree this node is in.
23508      * @return {Tree}
23509      */
23510     getOwnerTree : function(){
23511         // if it doesn't have one, look for one
23512         if(!this.ownerTree){
23513             var p = this;
23514             while(p){
23515                 if(p.ownerTree){
23516                     this.ownerTree = p.ownerTree;
23517                     break;
23518                 }
23519                 p = p.parentNode;
23520             }
23521         }
23522         return this.ownerTree;
23523     },
23524
23525     /**
23526      * Returns depth of this node (the root node has a depth of 0)
23527      * @return {Number}
23528      */
23529     getDepth : function(){
23530         var depth = 0;
23531         var p = this;
23532         while(p.parentNode){
23533             ++depth;
23534             p = p.parentNode;
23535         }
23536         return depth;
23537     },
23538
23539     // private
23540     setOwnerTree : function(tree){
23541         // if it's move, we need to update everyone
23542         if(tree != this.ownerTree){
23543             if(this.ownerTree){
23544                 this.ownerTree.unregisterNode(this);
23545             }
23546             this.ownerTree = tree;
23547             var cs = this.childNodes;
23548             for(var i = 0, len = cs.length; i < len; i++) {
23549                 cs[i].setOwnerTree(tree);
23550             }
23551             if(tree){
23552                 tree.registerNode(this);
23553             }
23554         }
23555     },
23556
23557     /**
23558      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23559      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23560      * @return {String} The path
23561      */
23562     getPath : function(attr){
23563         attr = attr || "id";
23564         var p = this.parentNode;
23565         var b = [this.attributes[attr]];
23566         while(p){
23567             b.unshift(p.attributes[attr]);
23568             p = p.parentNode;
23569         }
23570         var sep = this.getOwnerTree().pathSeparator;
23571         return sep + b.join(sep);
23572     },
23573
23574     /**
23575      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23576      * function call will be the scope provided or the current node. The arguments to the function
23577      * will be the args provided or the current node. If the function returns false at any point,
23578      * the bubble is stopped.
23579      * @param {Function} fn The function to call
23580      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23581      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23582      */
23583     bubble : function(fn, scope, args){
23584         var p = this;
23585         while(p){
23586             if(fn.call(scope || p, args || p) === false){
23587                 break;
23588             }
23589             p = p.parentNode;
23590         }
23591     },
23592
23593     /**
23594      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23595      * function call will be the scope provided or the current node. The arguments to the function
23596      * will be the args provided or the current node. If the function returns false at any point,
23597      * the cascade is stopped on that branch.
23598      * @param {Function} fn The function to call
23599      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23600      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23601      */
23602     cascade : function(fn, scope, args){
23603         if(fn.call(scope || this, args || this) !== false){
23604             var cs = this.childNodes;
23605             for(var i = 0, len = cs.length; i < len; i++) {
23606                 cs[i].cascade(fn, scope, args);
23607             }
23608         }
23609     },
23610
23611     /**
23612      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23613      * function call will be the scope provided or the current node. The arguments to the function
23614      * will be the args provided or the current node. If the function returns false at any point,
23615      * the iteration stops.
23616      * @param {Function} fn The function to call
23617      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23618      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23619      */
23620     eachChild : function(fn, scope, args){
23621         var cs = this.childNodes;
23622         for(var i = 0, len = cs.length; i < len; i++) {
23623                 if(fn.call(scope || this, args || cs[i]) === false){
23624                     break;
23625                 }
23626         }
23627     },
23628
23629     /**
23630      * Finds the first child that has the attribute with the specified value.
23631      * @param {String} attribute The attribute name
23632      * @param {Mixed} value The value to search for
23633      * @return {Node} The found child or null if none was found
23634      */
23635     findChild : function(attribute, value){
23636         var cs = this.childNodes;
23637         for(var i = 0, len = cs.length; i < len; i++) {
23638                 if(cs[i].attributes[attribute] == value){
23639                     return cs[i];
23640                 }
23641         }
23642         return null;
23643     },
23644
23645     /**
23646      * Finds the first child by a custom function. The child matches if the function passed
23647      * returns true.
23648      * @param {Function} fn
23649      * @param {Object} scope (optional)
23650      * @return {Node} The found child or null if none was found
23651      */
23652     findChildBy : function(fn, scope){
23653         var cs = this.childNodes;
23654         for(var i = 0, len = cs.length; i < len; i++) {
23655                 if(fn.call(scope||cs[i], cs[i]) === true){
23656                     return cs[i];
23657                 }
23658         }
23659         return null;
23660     },
23661
23662     /**
23663      * Sorts this nodes children using the supplied sort function
23664      * @param {Function} fn
23665      * @param {Object} scope (optional)
23666      */
23667     sort : function(fn, scope){
23668         var cs = this.childNodes;
23669         var len = cs.length;
23670         if(len > 0){
23671             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23672             cs.sort(sortFn);
23673             for(var i = 0; i < len; i++){
23674                 var n = cs[i];
23675                 n.previousSibling = cs[i-1];
23676                 n.nextSibling = cs[i+1];
23677                 if(i == 0){
23678                     this.setFirstChild(n);
23679                 }
23680                 if(i == len-1){
23681                     this.setLastChild(n);
23682                 }
23683             }
23684         }
23685     },
23686
23687     /**
23688      * Returns true if this node is an ancestor (at any point) of the passed node.
23689      * @param {Node} node
23690      * @return {Boolean}
23691      */
23692     contains : function(node){
23693         return node.isAncestor(this);
23694     },
23695
23696     /**
23697      * Returns true if the passed node is an ancestor (at any point) of this node.
23698      * @param {Node} node
23699      * @return {Boolean}
23700      */
23701     isAncestor : function(node){
23702         var p = this.parentNode;
23703         while(p){
23704             if(p == node){
23705                 return true;
23706             }
23707             p = p.parentNode;
23708         }
23709         return false;
23710     },
23711
23712     toString : function(){
23713         return "[Node"+(this.id?" "+this.id:"")+"]";
23714     }
23715 });/*
23716  * Based on:
23717  * Ext JS Library 1.1.1
23718  * Copyright(c) 2006-2007, Ext JS, LLC.
23719  *
23720  * Originally Released Under LGPL - original licence link has changed is not relivant.
23721  *
23722  * Fork - LGPL
23723  * <script type="text/javascript">
23724  */
23725  (function(){ 
23726 /**
23727  * @class Roo.Layer
23728  * @extends Roo.Element
23729  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23730  * automatic maintaining of shadow/shim positions.
23731  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23732  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23733  * you can pass a string with a CSS class name. False turns off the shadow.
23734  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23735  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23736  * @cfg {String} cls CSS class to add to the element
23737  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23738  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23739  * @constructor
23740  * @param {Object} config An object with config options.
23741  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23742  */
23743
23744 Roo.Layer = function(config, existingEl){
23745     config = config || {};
23746     var dh = Roo.DomHelper;
23747     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23748     if(existingEl){
23749         this.dom = Roo.getDom(existingEl);
23750     }
23751     if(!this.dom){
23752         var o = config.dh || {tag: "div", cls: "x-layer"};
23753         this.dom = dh.append(pel, o);
23754     }
23755     if(config.cls){
23756         this.addClass(config.cls);
23757     }
23758     this.constrain = config.constrain !== false;
23759     this.visibilityMode = Roo.Element.VISIBILITY;
23760     if(config.id){
23761         this.id = this.dom.id = config.id;
23762     }else{
23763         this.id = Roo.id(this.dom);
23764     }
23765     this.zindex = config.zindex || this.getZIndex();
23766     this.position("absolute", this.zindex);
23767     if(config.shadow){
23768         this.shadowOffset = config.shadowOffset || 4;
23769         this.shadow = new Roo.Shadow({
23770             offset : this.shadowOffset,
23771             mode : config.shadow
23772         });
23773     }else{
23774         this.shadowOffset = 0;
23775     }
23776     this.useShim = config.shim !== false && Roo.useShims;
23777     this.useDisplay = config.useDisplay;
23778     this.hide();
23779 };
23780
23781 var supr = Roo.Element.prototype;
23782
23783 // shims are shared among layer to keep from having 100 iframes
23784 var shims = [];
23785
23786 Roo.extend(Roo.Layer, Roo.Element, {
23787
23788     getZIndex : function(){
23789         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23790     },
23791
23792     getShim : function(){
23793         if(!this.useShim){
23794             return null;
23795         }
23796         if(this.shim){
23797             return this.shim;
23798         }
23799         var shim = shims.shift();
23800         if(!shim){
23801             shim = this.createShim();
23802             shim.enableDisplayMode('block');
23803             shim.dom.style.display = 'none';
23804             shim.dom.style.visibility = 'visible';
23805         }
23806         var pn = this.dom.parentNode;
23807         if(shim.dom.parentNode != pn){
23808             pn.insertBefore(shim.dom, this.dom);
23809         }
23810         shim.setStyle('z-index', this.getZIndex()-2);
23811         this.shim = shim;
23812         return shim;
23813     },
23814
23815     hideShim : function(){
23816         if(this.shim){
23817             this.shim.setDisplayed(false);
23818             shims.push(this.shim);
23819             delete this.shim;
23820         }
23821     },
23822
23823     disableShadow : function(){
23824         if(this.shadow){
23825             this.shadowDisabled = true;
23826             this.shadow.hide();
23827             this.lastShadowOffset = this.shadowOffset;
23828             this.shadowOffset = 0;
23829         }
23830     },
23831
23832     enableShadow : function(show){
23833         if(this.shadow){
23834             this.shadowDisabled = false;
23835             this.shadowOffset = this.lastShadowOffset;
23836             delete this.lastShadowOffset;
23837             if(show){
23838                 this.sync(true);
23839             }
23840         }
23841     },
23842
23843     // private
23844     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23845     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23846     sync : function(doShow){
23847         var sw = this.shadow;
23848         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23849             var sh = this.getShim();
23850
23851             var w = this.getWidth(),
23852                 h = this.getHeight();
23853
23854             var l = this.getLeft(true),
23855                 t = this.getTop(true);
23856
23857             if(sw && !this.shadowDisabled){
23858                 if(doShow && !sw.isVisible()){
23859                     sw.show(this);
23860                 }else{
23861                     sw.realign(l, t, w, h);
23862                 }
23863                 if(sh){
23864                     if(doShow){
23865                        sh.show();
23866                     }
23867                     // fit the shim behind the shadow, so it is shimmed too
23868                     var a = sw.adjusts, s = sh.dom.style;
23869                     s.left = (Math.min(l, l+a.l))+"px";
23870                     s.top = (Math.min(t, t+a.t))+"px";
23871                     s.width = (w+a.w)+"px";
23872                     s.height = (h+a.h)+"px";
23873                 }
23874             }else if(sh){
23875                 if(doShow){
23876                    sh.show();
23877                 }
23878                 sh.setSize(w, h);
23879                 sh.setLeftTop(l, t);
23880             }
23881             
23882         }
23883     },
23884
23885     // private
23886     destroy : function(){
23887         this.hideShim();
23888         if(this.shadow){
23889             this.shadow.hide();
23890         }
23891         this.removeAllListeners();
23892         var pn = this.dom.parentNode;
23893         if(pn){
23894             pn.removeChild(this.dom);
23895         }
23896         Roo.Element.uncache(this.id);
23897     },
23898
23899     remove : function(){
23900         this.destroy();
23901     },
23902
23903     // private
23904     beginUpdate : function(){
23905         this.updating = true;
23906     },
23907
23908     // private
23909     endUpdate : function(){
23910         this.updating = false;
23911         this.sync(true);
23912     },
23913
23914     // private
23915     hideUnders : function(negOffset){
23916         if(this.shadow){
23917             this.shadow.hide();
23918         }
23919         this.hideShim();
23920     },
23921
23922     // private
23923     constrainXY : function(){
23924         if(this.constrain){
23925             var vw = Roo.lib.Dom.getViewWidth(),
23926                 vh = Roo.lib.Dom.getViewHeight();
23927             var s = Roo.get(document).getScroll();
23928
23929             var xy = this.getXY();
23930             var x = xy[0], y = xy[1];   
23931             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23932             // only move it if it needs it
23933             var moved = false;
23934             // first validate right/bottom
23935             if((x + w) > vw+s.left){
23936                 x = vw - w - this.shadowOffset;
23937                 moved = true;
23938             }
23939             if((y + h) > vh+s.top){
23940                 y = vh - h - this.shadowOffset;
23941                 moved = true;
23942             }
23943             // then make sure top/left isn't negative
23944             if(x < s.left){
23945                 x = s.left;
23946                 moved = true;
23947             }
23948             if(y < s.top){
23949                 y = s.top;
23950                 moved = true;
23951             }
23952             if(moved){
23953                 if(this.avoidY){
23954                     var ay = this.avoidY;
23955                     if(y <= ay && (y+h) >= ay){
23956                         y = ay-h-5;   
23957                     }
23958                 }
23959                 xy = [x, y];
23960                 this.storeXY(xy);
23961                 supr.setXY.call(this, xy);
23962                 this.sync();
23963             }
23964         }
23965     },
23966
23967     isVisible : function(){
23968         return this.visible;    
23969     },
23970
23971     // private
23972     showAction : function(){
23973         this.visible = true; // track visibility to prevent getStyle calls
23974         if(this.useDisplay === true){
23975             this.setDisplayed("");
23976         }else if(this.lastXY){
23977             supr.setXY.call(this, this.lastXY);
23978         }else if(this.lastLT){
23979             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23980         }
23981     },
23982
23983     // private
23984     hideAction : function(){
23985         this.visible = false;
23986         if(this.useDisplay === true){
23987             this.setDisplayed(false);
23988         }else{
23989             this.setLeftTop(-10000,-10000);
23990         }
23991     },
23992
23993     // overridden Element method
23994     setVisible : function(v, a, d, c, e){
23995         if(v){
23996             this.showAction();
23997         }
23998         if(a && v){
23999             var cb = function(){
24000                 this.sync(true);
24001                 if(c){
24002                     c();
24003                 }
24004             }.createDelegate(this);
24005             supr.setVisible.call(this, true, true, d, cb, e);
24006         }else{
24007             if(!v){
24008                 this.hideUnders(true);
24009             }
24010             var cb = c;
24011             if(a){
24012                 cb = function(){
24013                     this.hideAction();
24014                     if(c){
24015                         c();
24016                     }
24017                 }.createDelegate(this);
24018             }
24019             supr.setVisible.call(this, v, a, d, cb, e);
24020             if(v){
24021                 this.sync(true);
24022             }else if(!a){
24023                 this.hideAction();
24024             }
24025         }
24026     },
24027
24028     storeXY : function(xy){
24029         delete this.lastLT;
24030         this.lastXY = xy;
24031     },
24032
24033     storeLeftTop : function(left, top){
24034         delete this.lastXY;
24035         this.lastLT = [left, top];
24036     },
24037
24038     // private
24039     beforeFx : function(){
24040         this.beforeAction();
24041         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24042     },
24043
24044     // private
24045     afterFx : function(){
24046         Roo.Layer.superclass.afterFx.apply(this, arguments);
24047         this.sync(this.isVisible());
24048     },
24049
24050     // private
24051     beforeAction : function(){
24052         if(!this.updating && this.shadow){
24053             this.shadow.hide();
24054         }
24055     },
24056
24057     // overridden Element method
24058     setLeft : function(left){
24059         this.storeLeftTop(left, this.getTop(true));
24060         supr.setLeft.apply(this, arguments);
24061         this.sync();
24062     },
24063
24064     setTop : function(top){
24065         this.storeLeftTop(this.getLeft(true), top);
24066         supr.setTop.apply(this, arguments);
24067         this.sync();
24068     },
24069
24070     setLeftTop : function(left, top){
24071         this.storeLeftTop(left, top);
24072         supr.setLeftTop.apply(this, arguments);
24073         this.sync();
24074     },
24075
24076     setXY : function(xy, a, d, c, e){
24077         this.fixDisplay();
24078         this.beforeAction();
24079         this.storeXY(xy);
24080         var cb = this.createCB(c);
24081         supr.setXY.call(this, xy, a, d, cb, e);
24082         if(!a){
24083             cb();
24084         }
24085     },
24086
24087     // private
24088     createCB : function(c){
24089         var el = this;
24090         return function(){
24091             el.constrainXY();
24092             el.sync(true);
24093             if(c){
24094                 c();
24095             }
24096         };
24097     },
24098
24099     // overridden Element method
24100     setX : function(x, a, d, c, e){
24101         this.setXY([x, this.getY()], a, d, c, e);
24102     },
24103
24104     // overridden Element method
24105     setY : function(y, a, d, c, e){
24106         this.setXY([this.getX(), y], a, d, c, e);
24107     },
24108
24109     // overridden Element method
24110     setSize : function(w, h, a, d, c, e){
24111         this.beforeAction();
24112         var cb = this.createCB(c);
24113         supr.setSize.call(this, w, h, a, d, cb, e);
24114         if(!a){
24115             cb();
24116         }
24117     },
24118
24119     // overridden Element method
24120     setWidth : function(w, a, d, c, e){
24121         this.beforeAction();
24122         var cb = this.createCB(c);
24123         supr.setWidth.call(this, w, a, d, cb, e);
24124         if(!a){
24125             cb();
24126         }
24127     },
24128
24129     // overridden Element method
24130     setHeight : function(h, a, d, c, e){
24131         this.beforeAction();
24132         var cb = this.createCB(c);
24133         supr.setHeight.call(this, h, a, d, cb, e);
24134         if(!a){
24135             cb();
24136         }
24137     },
24138
24139     // overridden Element method
24140     setBounds : function(x, y, w, h, a, d, c, e){
24141         this.beforeAction();
24142         var cb = this.createCB(c);
24143         if(!a){
24144             this.storeXY([x, y]);
24145             supr.setXY.call(this, [x, y]);
24146             supr.setSize.call(this, w, h, a, d, cb, e);
24147             cb();
24148         }else{
24149             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24150         }
24151         return this;
24152     },
24153     
24154     /**
24155      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24156      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24157      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24158      * @param {Number} zindex The new z-index to set
24159      * @return {this} The Layer
24160      */
24161     setZIndex : function(zindex){
24162         this.zindex = zindex;
24163         this.setStyle("z-index", zindex + 2);
24164         if(this.shadow){
24165             this.shadow.setZIndex(zindex + 1);
24166         }
24167         if(this.shim){
24168             this.shim.setStyle("z-index", zindex);
24169         }
24170     }
24171 });
24172 })();/*
24173  * Based on:
24174  * Ext JS Library 1.1.1
24175  * Copyright(c) 2006-2007, Ext JS, LLC.
24176  *
24177  * Originally Released Under LGPL - original licence link has changed is not relivant.
24178  *
24179  * Fork - LGPL
24180  * <script type="text/javascript">
24181  */
24182
24183
24184 /**
24185  * @class Roo.Shadow
24186  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24187  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24188  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24189  * @constructor
24190  * Create a new Shadow
24191  * @param {Object} config The config object
24192  */
24193 Roo.Shadow = function(config){
24194     Roo.apply(this, config);
24195     if(typeof this.mode != "string"){
24196         this.mode = this.defaultMode;
24197     }
24198     var o = this.offset, a = {h: 0};
24199     var rad = Math.floor(this.offset/2);
24200     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24201         case "drop":
24202             a.w = 0;
24203             a.l = a.t = o;
24204             a.t -= 1;
24205             if(Roo.isIE){
24206                 a.l -= this.offset + rad;
24207                 a.t -= this.offset + rad;
24208                 a.w -= rad;
24209                 a.h -= rad;
24210                 a.t += 1;
24211             }
24212         break;
24213         case "sides":
24214             a.w = (o*2);
24215             a.l = -o;
24216             a.t = o-1;
24217             if(Roo.isIE){
24218                 a.l -= (this.offset - rad);
24219                 a.t -= this.offset + rad;
24220                 a.l += 1;
24221                 a.w -= (this.offset - rad)*2;
24222                 a.w -= rad + 1;
24223                 a.h -= 1;
24224             }
24225         break;
24226         case "frame":
24227             a.w = a.h = (o*2);
24228             a.l = a.t = -o;
24229             a.t += 1;
24230             a.h -= 2;
24231             if(Roo.isIE){
24232                 a.l -= (this.offset - rad);
24233                 a.t -= (this.offset - rad);
24234                 a.l += 1;
24235                 a.w -= (this.offset + rad + 1);
24236                 a.h -= (this.offset + rad);
24237                 a.h += 1;
24238             }
24239         break;
24240     };
24241
24242     this.adjusts = a;
24243 };
24244
24245 Roo.Shadow.prototype = {
24246     /**
24247      * @cfg {String} mode
24248      * The shadow display mode.  Supports the following options:<br />
24249      * sides: Shadow displays on both sides and bottom only<br />
24250      * frame: Shadow displays equally on all four sides<br />
24251      * drop: Traditional bottom-right drop shadow (default)
24252      */
24253     /**
24254      * @cfg {String} offset
24255      * The number of pixels to offset the shadow from the element (defaults to 4)
24256      */
24257     offset: 4,
24258
24259     // private
24260     defaultMode: "drop",
24261
24262     /**
24263      * Displays the shadow under the target element
24264      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24265      */
24266     show : function(target){
24267         target = Roo.get(target);
24268         if(!this.el){
24269             this.el = Roo.Shadow.Pool.pull();
24270             if(this.el.dom.nextSibling != target.dom){
24271                 this.el.insertBefore(target);
24272             }
24273         }
24274         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24275         if(Roo.isIE){
24276             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24277         }
24278         this.realign(
24279             target.getLeft(true),
24280             target.getTop(true),
24281             target.getWidth(),
24282             target.getHeight()
24283         );
24284         this.el.dom.style.display = "block";
24285     },
24286
24287     /**
24288      * Returns true if the shadow is visible, else false
24289      */
24290     isVisible : function(){
24291         return this.el ? true : false;  
24292     },
24293
24294     /**
24295      * Direct alignment when values are already available. Show must be called at least once before
24296      * calling this method to ensure it is initialized.
24297      * @param {Number} left The target element left position
24298      * @param {Number} top The target element top position
24299      * @param {Number} width The target element width
24300      * @param {Number} height The target element height
24301      */
24302     realign : function(l, t, w, h){
24303         if(!this.el){
24304             return;
24305         }
24306         var a = this.adjusts, d = this.el.dom, s = d.style;
24307         var iea = 0;
24308         s.left = (l+a.l)+"px";
24309         s.top = (t+a.t)+"px";
24310         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24311  
24312         if(s.width != sws || s.height != shs){
24313             s.width = sws;
24314             s.height = shs;
24315             if(!Roo.isIE){
24316                 var cn = d.childNodes;
24317                 var sww = Math.max(0, (sw-12))+"px";
24318                 cn[0].childNodes[1].style.width = sww;
24319                 cn[1].childNodes[1].style.width = sww;
24320                 cn[2].childNodes[1].style.width = sww;
24321                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24322             }
24323         }
24324     },
24325
24326     /**
24327      * Hides this shadow
24328      */
24329     hide : function(){
24330         if(this.el){
24331             this.el.dom.style.display = "none";
24332             Roo.Shadow.Pool.push(this.el);
24333             delete this.el;
24334         }
24335     },
24336
24337     /**
24338      * Adjust the z-index of this shadow
24339      * @param {Number} zindex The new z-index
24340      */
24341     setZIndex : function(z){
24342         this.zIndex = z;
24343         if(this.el){
24344             this.el.setStyle("z-index", z);
24345         }
24346     }
24347 };
24348
24349 // Private utility class that manages the internal Shadow cache
24350 Roo.Shadow.Pool = function(){
24351     var p = [];
24352     var markup = Roo.isIE ?
24353                  '<div class="x-ie-shadow"></div>' :
24354                  '<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>';
24355     return {
24356         pull : function(){
24357             var sh = p.shift();
24358             if(!sh){
24359                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24360                 sh.autoBoxAdjust = false;
24361             }
24362             return sh;
24363         },
24364
24365         push : function(sh){
24366             p.push(sh);
24367         }
24368     };
24369 }();/*
24370  * Based on:
24371  * Ext JS Library 1.1.1
24372  * Copyright(c) 2006-2007, Ext JS, LLC.
24373  *
24374  * Originally Released Under LGPL - original licence link has changed is not relivant.
24375  *
24376  * Fork - LGPL
24377  * <script type="text/javascript">
24378  */
24379
24380
24381 /**
24382  * @class Roo.SplitBar
24383  * @extends Roo.util.Observable
24384  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24385  * <br><br>
24386  * Usage:
24387  * <pre><code>
24388 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24389                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24390 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24391 split.minSize = 100;
24392 split.maxSize = 600;
24393 split.animate = true;
24394 split.on('moved', splitterMoved);
24395 </code></pre>
24396  * @constructor
24397  * Create a new SplitBar
24398  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24399  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24400  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24401  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24402                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24403                         position of the SplitBar).
24404  */
24405 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24406     
24407     /** @private */
24408     this.el = Roo.get(dragElement, true);
24409     this.el.dom.unselectable = "on";
24410     /** @private */
24411     this.resizingEl = Roo.get(resizingElement, true);
24412
24413     /**
24414      * @private
24415      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24416      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24417      * @type Number
24418      */
24419     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24420     
24421     /**
24422      * The minimum size of the resizing element. (Defaults to 0)
24423      * @type Number
24424      */
24425     this.minSize = 0;
24426     
24427     /**
24428      * The maximum size of the resizing element. (Defaults to 2000)
24429      * @type Number
24430      */
24431     this.maxSize = 2000;
24432     
24433     /**
24434      * Whether to animate the transition to the new size
24435      * @type Boolean
24436      */
24437     this.animate = false;
24438     
24439     /**
24440      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24441      * @type Boolean
24442      */
24443     this.useShim = false;
24444     
24445     /** @private */
24446     this.shim = null;
24447     
24448     if(!existingProxy){
24449         /** @private */
24450         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24451     }else{
24452         this.proxy = Roo.get(existingProxy).dom;
24453     }
24454     /** @private */
24455     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24456     
24457     /** @private */
24458     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24459     
24460     /** @private */
24461     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24462     
24463     /** @private */
24464     this.dragSpecs = {};
24465     
24466     /**
24467      * @private The adapter to use to positon and resize elements
24468      */
24469     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24470     this.adapter.init(this);
24471     
24472     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24473         /** @private */
24474         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24475         this.el.addClass("x-splitbar-h");
24476     }else{
24477         /** @private */
24478         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24479         this.el.addClass("x-splitbar-v");
24480     }
24481     
24482     this.addEvents({
24483         /**
24484          * @event resize
24485          * Fires when the splitter is moved (alias for {@link #event-moved})
24486          * @param {Roo.SplitBar} this
24487          * @param {Number} newSize the new width or height
24488          */
24489         "resize" : true,
24490         /**
24491          * @event moved
24492          * Fires when the splitter is moved
24493          * @param {Roo.SplitBar} this
24494          * @param {Number} newSize the new width or height
24495          */
24496         "moved" : true,
24497         /**
24498          * @event beforeresize
24499          * Fires before the splitter is dragged
24500          * @param {Roo.SplitBar} this
24501          */
24502         "beforeresize" : true,
24503
24504         "beforeapply" : true
24505     });
24506
24507     Roo.util.Observable.call(this);
24508 };
24509
24510 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24511     onStartProxyDrag : function(x, y){
24512         this.fireEvent("beforeresize", this);
24513         if(!this.overlay){
24514             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24515             o.unselectable();
24516             o.enableDisplayMode("block");
24517             // all splitbars share the same overlay
24518             Roo.SplitBar.prototype.overlay = o;
24519         }
24520         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24521         this.overlay.show();
24522         Roo.get(this.proxy).setDisplayed("block");
24523         var size = this.adapter.getElementSize(this);
24524         this.activeMinSize = this.getMinimumSize();;
24525         this.activeMaxSize = this.getMaximumSize();;
24526         var c1 = size - this.activeMinSize;
24527         var c2 = Math.max(this.activeMaxSize - size, 0);
24528         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24529             this.dd.resetConstraints();
24530             this.dd.setXConstraint(
24531                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24532                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24533             );
24534             this.dd.setYConstraint(0, 0);
24535         }else{
24536             this.dd.resetConstraints();
24537             this.dd.setXConstraint(0, 0);
24538             this.dd.setYConstraint(
24539                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24540                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24541             );
24542          }
24543         this.dragSpecs.startSize = size;
24544         this.dragSpecs.startPoint = [x, y];
24545         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24546     },
24547     
24548     /** 
24549      * @private Called after the drag operation by the DDProxy
24550      */
24551     onEndProxyDrag : function(e){
24552         Roo.get(this.proxy).setDisplayed(false);
24553         var endPoint = Roo.lib.Event.getXY(e);
24554         if(this.overlay){
24555             this.overlay.hide();
24556         }
24557         var newSize;
24558         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24559             newSize = this.dragSpecs.startSize + 
24560                 (this.placement == Roo.SplitBar.LEFT ?
24561                     endPoint[0] - this.dragSpecs.startPoint[0] :
24562                     this.dragSpecs.startPoint[0] - endPoint[0]
24563                 );
24564         }else{
24565             newSize = this.dragSpecs.startSize + 
24566                 (this.placement == Roo.SplitBar.TOP ?
24567                     endPoint[1] - this.dragSpecs.startPoint[1] :
24568                     this.dragSpecs.startPoint[1] - endPoint[1]
24569                 );
24570         }
24571         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24572         if(newSize != this.dragSpecs.startSize){
24573             if(this.fireEvent('beforeapply', this, newSize) !== false){
24574                 this.adapter.setElementSize(this, newSize);
24575                 this.fireEvent("moved", this, newSize);
24576                 this.fireEvent("resize", this, newSize);
24577             }
24578         }
24579     },
24580     
24581     /**
24582      * Get the adapter this SplitBar uses
24583      * @return The adapter object
24584      */
24585     getAdapter : function(){
24586         return this.adapter;
24587     },
24588     
24589     /**
24590      * Set the adapter this SplitBar uses
24591      * @param {Object} adapter A SplitBar adapter object
24592      */
24593     setAdapter : function(adapter){
24594         this.adapter = adapter;
24595         this.adapter.init(this);
24596     },
24597     
24598     /**
24599      * Gets the minimum size for the resizing element
24600      * @return {Number} The minimum size
24601      */
24602     getMinimumSize : function(){
24603         return this.minSize;
24604     },
24605     
24606     /**
24607      * Sets the minimum size for the resizing element
24608      * @param {Number} minSize The minimum size
24609      */
24610     setMinimumSize : function(minSize){
24611         this.minSize = minSize;
24612     },
24613     
24614     /**
24615      * Gets the maximum size for the resizing element
24616      * @return {Number} The maximum size
24617      */
24618     getMaximumSize : function(){
24619         return this.maxSize;
24620     },
24621     
24622     /**
24623      * Sets the maximum size for the resizing element
24624      * @param {Number} maxSize The maximum size
24625      */
24626     setMaximumSize : function(maxSize){
24627         this.maxSize = maxSize;
24628     },
24629     
24630     /**
24631      * Sets the initialize size for the resizing element
24632      * @param {Number} size The initial size
24633      */
24634     setCurrentSize : function(size){
24635         var oldAnimate = this.animate;
24636         this.animate = false;
24637         this.adapter.setElementSize(this, size);
24638         this.animate = oldAnimate;
24639     },
24640     
24641     /**
24642      * Destroy this splitbar. 
24643      * @param {Boolean} removeEl True to remove the element
24644      */
24645     destroy : function(removeEl){
24646         if(this.shim){
24647             this.shim.remove();
24648         }
24649         this.dd.unreg();
24650         this.proxy.parentNode.removeChild(this.proxy);
24651         if(removeEl){
24652             this.el.remove();
24653         }
24654     }
24655 });
24656
24657 /**
24658  * @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.
24659  */
24660 Roo.SplitBar.createProxy = function(dir){
24661     var proxy = new Roo.Element(document.createElement("div"));
24662     proxy.unselectable();
24663     var cls = 'x-splitbar-proxy';
24664     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24665     document.body.appendChild(proxy.dom);
24666     return proxy.dom;
24667 };
24668
24669 /** 
24670  * @class Roo.SplitBar.BasicLayoutAdapter
24671  * Default Adapter. It assumes the splitter and resizing element are not positioned
24672  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24673  */
24674 Roo.SplitBar.BasicLayoutAdapter = function(){
24675 };
24676
24677 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24678     // do nothing for now
24679     init : function(s){
24680     
24681     },
24682     /**
24683      * Called before drag operations to get the current size of the resizing element. 
24684      * @param {Roo.SplitBar} s The SplitBar using this adapter
24685      */
24686      getElementSize : function(s){
24687         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24688             return s.resizingEl.getWidth();
24689         }else{
24690             return s.resizingEl.getHeight();
24691         }
24692     },
24693     
24694     /**
24695      * Called after drag operations to set the size of the resizing element.
24696      * @param {Roo.SplitBar} s The SplitBar using this adapter
24697      * @param {Number} newSize The new size to set
24698      * @param {Function} onComplete A function to be invoked when resizing is complete
24699      */
24700     setElementSize : function(s, newSize, onComplete){
24701         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24702             if(!s.animate){
24703                 s.resizingEl.setWidth(newSize);
24704                 if(onComplete){
24705                     onComplete(s, newSize);
24706                 }
24707             }else{
24708                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24709             }
24710         }else{
24711             
24712             if(!s.animate){
24713                 s.resizingEl.setHeight(newSize);
24714                 if(onComplete){
24715                     onComplete(s, newSize);
24716                 }
24717             }else{
24718                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24719             }
24720         }
24721     }
24722 };
24723
24724 /** 
24725  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24726  * @extends Roo.SplitBar.BasicLayoutAdapter
24727  * Adapter that  moves the splitter element to align with the resized sizing element. 
24728  * Used with an absolute positioned SplitBar.
24729  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24730  * document.body, make sure you assign an id to the body element.
24731  */
24732 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24733     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24734     this.container = Roo.get(container);
24735 };
24736
24737 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24738     init : function(s){
24739         this.basic.init(s);
24740     },
24741     
24742     getElementSize : function(s){
24743         return this.basic.getElementSize(s);
24744     },
24745     
24746     setElementSize : function(s, newSize, onComplete){
24747         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24748     },
24749     
24750     moveSplitter : function(s){
24751         var yes = Roo.SplitBar;
24752         switch(s.placement){
24753             case yes.LEFT:
24754                 s.el.setX(s.resizingEl.getRight());
24755                 break;
24756             case yes.RIGHT:
24757                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24758                 break;
24759             case yes.TOP:
24760                 s.el.setY(s.resizingEl.getBottom());
24761                 break;
24762             case yes.BOTTOM:
24763                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24764                 break;
24765         }
24766     }
24767 };
24768
24769 /**
24770  * Orientation constant - Create a vertical SplitBar
24771  * @static
24772  * @type Number
24773  */
24774 Roo.SplitBar.VERTICAL = 1;
24775
24776 /**
24777  * Orientation constant - Create a horizontal SplitBar
24778  * @static
24779  * @type Number
24780  */
24781 Roo.SplitBar.HORIZONTAL = 2;
24782
24783 /**
24784  * Placement constant - The resizing element is to the left of the splitter element
24785  * @static
24786  * @type Number
24787  */
24788 Roo.SplitBar.LEFT = 1;
24789
24790 /**
24791  * Placement constant - The resizing element is to the right of the splitter element
24792  * @static
24793  * @type Number
24794  */
24795 Roo.SplitBar.RIGHT = 2;
24796
24797 /**
24798  * Placement constant - The resizing element is positioned above the splitter element
24799  * @static
24800  * @type Number
24801  */
24802 Roo.SplitBar.TOP = 3;
24803
24804 /**
24805  * Placement constant - The resizing element is positioned under splitter element
24806  * @static
24807  * @type Number
24808  */
24809 Roo.SplitBar.BOTTOM = 4;
24810 /*
24811  * Based on:
24812  * Ext JS Library 1.1.1
24813  * Copyright(c) 2006-2007, Ext JS, LLC.
24814  *
24815  * Originally Released Under LGPL - original licence link has changed is not relivant.
24816  *
24817  * Fork - LGPL
24818  * <script type="text/javascript">
24819  */
24820
24821 /**
24822  * @class Roo.View
24823  * @extends Roo.util.Observable
24824  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24825  * This class also supports single and multi selection modes. <br>
24826  * Create a data model bound view:
24827  <pre><code>
24828  var store = new Roo.data.Store(...);
24829
24830  var view = new Roo.View({
24831     el : "my-element",
24832     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24833  
24834     singleSelect: true,
24835     selectedClass: "ydataview-selected",
24836     store: store
24837  });
24838
24839  // listen for node click?
24840  view.on("click", function(vw, index, node, e){
24841  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24842  });
24843
24844  // load XML data
24845  dataModel.load("foobar.xml");
24846  </code></pre>
24847  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24848  * <br><br>
24849  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24850  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24851  * 
24852  * Note: old style constructor is still suported (container, template, config)
24853  * 
24854  * @constructor
24855  * Create a new View
24856  * @param {Object} config The config object
24857  * 
24858  */
24859 Roo.View = function(config, depreciated_tpl, depreciated_config){
24860     
24861     this.parent = false;
24862     
24863     if (typeof(depreciated_tpl) == 'undefined') {
24864         // new way.. - universal constructor.
24865         Roo.apply(this, config);
24866         this.el  = Roo.get(this.el);
24867     } else {
24868         // old format..
24869         this.el  = Roo.get(config);
24870         this.tpl = depreciated_tpl;
24871         Roo.apply(this, depreciated_config);
24872     }
24873     this.wrapEl  = this.el.wrap().wrap();
24874     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24875     
24876     
24877     if(typeof(this.tpl) == "string"){
24878         this.tpl = new Roo.Template(this.tpl);
24879     } else {
24880         // support xtype ctors..
24881         this.tpl = new Roo.factory(this.tpl, Roo);
24882     }
24883     
24884     
24885     this.tpl.compile();
24886     
24887     /** @private */
24888     this.addEvents({
24889         /**
24890          * @event beforeclick
24891          * Fires before a click is processed. Returns false to cancel the default action.
24892          * @param {Roo.View} this
24893          * @param {Number} index The index of the target node
24894          * @param {HTMLElement} node The target node
24895          * @param {Roo.EventObject} e The raw event object
24896          */
24897             "beforeclick" : true,
24898         /**
24899          * @event click
24900          * Fires when a template node is clicked.
24901          * @param {Roo.View} this
24902          * @param {Number} index The index of the target node
24903          * @param {HTMLElement} node The target node
24904          * @param {Roo.EventObject} e The raw event object
24905          */
24906             "click" : true,
24907         /**
24908          * @event dblclick
24909          * Fires when a template node is double clicked.
24910          * @param {Roo.View} this
24911          * @param {Number} index The index of the target node
24912          * @param {HTMLElement} node The target node
24913          * @param {Roo.EventObject} e The raw event object
24914          */
24915             "dblclick" : true,
24916         /**
24917          * @event contextmenu
24918          * Fires when a template node is right clicked.
24919          * @param {Roo.View} this
24920          * @param {Number} index The index of the target node
24921          * @param {HTMLElement} node The target node
24922          * @param {Roo.EventObject} e The raw event object
24923          */
24924             "contextmenu" : true,
24925         /**
24926          * @event selectionchange
24927          * Fires when the selected nodes change.
24928          * @param {Roo.View} this
24929          * @param {Array} selections Array of the selected nodes
24930          */
24931             "selectionchange" : true,
24932     
24933         /**
24934          * @event beforeselect
24935          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24936          * @param {Roo.View} this
24937          * @param {HTMLElement} node The node to be selected
24938          * @param {Array} selections Array of currently selected nodes
24939          */
24940             "beforeselect" : true,
24941         /**
24942          * @event preparedata
24943          * Fires on every row to render, to allow you to change the data.
24944          * @param {Roo.View} this
24945          * @param {Object} data to be rendered (change this)
24946          */
24947           "preparedata" : true
24948           
24949           
24950         });
24951
24952
24953
24954     this.el.on({
24955         "click": this.onClick,
24956         "dblclick": this.onDblClick,
24957         "contextmenu": this.onContextMenu,
24958         scope:this
24959     });
24960
24961     this.selections = [];
24962     this.nodes = [];
24963     this.cmp = new Roo.CompositeElementLite([]);
24964     if(this.store){
24965         this.store = Roo.factory(this.store, Roo.data);
24966         this.setStore(this.store, true);
24967     }
24968     
24969     if ( this.footer && this.footer.xtype) {
24970            
24971          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24972         
24973         this.footer.dataSource = this.store
24974         this.footer.container = fctr;
24975         this.footer = Roo.factory(this.footer, Roo);
24976         fctr.insertFirst(this.el);
24977         
24978         // this is a bit insane - as the paging toolbar seems to detach the el..
24979 //        dom.parentNode.parentNode.parentNode
24980          // they get detached?
24981     }
24982     
24983     
24984     Roo.View.superclass.constructor.call(this);
24985     
24986     
24987 };
24988
24989 Roo.extend(Roo.View, Roo.util.Observable, {
24990     
24991      /**
24992      * @cfg {Roo.data.Store} store Data store to load data from.
24993      */
24994     store : false,
24995     
24996     /**
24997      * @cfg {String|Roo.Element} el The container element.
24998      */
24999     el : '',
25000     
25001     /**
25002      * @cfg {String|Roo.Template} tpl The template used by this View 
25003      */
25004     tpl : false,
25005     /**
25006      * @cfg {String} dataName the named area of the template to use as the data area
25007      *                          Works with domtemplates roo-name="name"
25008      */
25009     dataName: false,
25010     /**
25011      * @cfg {String} selectedClass The css class to add to selected nodes
25012      */
25013     selectedClass : "x-view-selected",
25014      /**
25015      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25016      */
25017     emptyText : "",
25018     
25019     /**
25020      * @cfg {String} text to display on mask (default Loading)
25021      */
25022     mask : false,
25023     /**
25024      * @cfg {Boolean} multiSelect Allow multiple selection
25025      */
25026     multiSelect : false,
25027     /**
25028      * @cfg {Boolean} singleSelect Allow single selection
25029      */
25030     singleSelect:  false,
25031     
25032     /**
25033      * @cfg {Boolean} toggleSelect - selecting 
25034      */
25035     toggleSelect : false,
25036     
25037     /**
25038      * @cfg {Boolean} tickable - selecting 
25039      */
25040     tickable : false,
25041     
25042     /**
25043      * Returns the element this view is bound to.
25044      * @return {Roo.Element}
25045      */
25046     getEl : function(){
25047         return this.wrapEl;
25048     },
25049     
25050     
25051
25052     /**
25053      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25054      */
25055     refresh : function(){
25056         //Roo.log('refresh');
25057         var t = this.tpl;
25058         
25059         // if we are using something like 'domtemplate', then
25060         // the what gets used is:
25061         // t.applySubtemplate(NAME, data, wrapping data..)
25062         // the outer template then get' applied with
25063         //     the store 'extra data'
25064         // and the body get's added to the
25065         //      roo-name="data" node?
25066         //      <span class='roo-tpl-{name}'></span> ?????
25067         
25068         
25069         
25070         this.clearSelections();
25071         this.el.update("");
25072         var html = [];
25073         var records = this.store.getRange();
25074         if(records.length < 1) {
25075             
25076             // is this valid??  = should it render a template??
25077             
25078             this.el.update(this.emptyText);
25079             return;
25080         }
25081         var el = this.el;
25082         if (this.dataName) {
25083             this.el.update(t.apply(this.store.meta)); //????
25084             el = this.el.child('.roo-tpl-' + this.dataName);
25085         }
25086         
25087         for(var i = 0, len = records.length; i < len; i++){
25088             var data = this.prepareData(records[i].data, i, records[i]);
25089             this.fireEvent("preparedata", this, data, i, records[i]);
25090             
25091             var d = Roo.apply({}, data);
25092             
25093             if(this.tickable){
25094                 Roo.apply(d, {'roo-id' : Roo.id()});
25095                 
25096                 var _this = this;
25097             
25098                 Roo.each(this.parent.item, function(item){
25099                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25100                         return;
25101                     }
25102                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25103                 });
25104             }
25105             
25106             html[html.length] = Roo.util.Format.trim(
25107                 this.dataName ?
25108                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25109                     t.apply(d)
25110             );
25111         }
25112         
25113         
25114         
25115         el.update(html.join(""));
25116         this.nodes = el.dom.childNodes;
25117         this.updateIndexes(0);
25118     },
25119     
25120
25121     /**
25122      * Function to override to reformat the data that is sent to
25123      * the template for each node.
25124      * DEPRICATED - use the preparedata event handler.
25125      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25126      * a JSON object for an UpdateManager bound view).
25127      */
25128     prepareData : function(data, index, record)
25129     {
25130         this.fireEvent("preparedata", this, data, index, record);
25131         return data;
25132     },
25133
25134     onUpdate : function(ds, record){
25135         // Roo.log('on update');   
25136         this.clearSelections();
25137         var index = this.store.indexOf(record);
25138         var n = this.nodes[index];
25139         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25140         n.parentNode.removeChild(n);
25141         this.updateIndexes(index, index);
25142     },
25143
25144     
25145     
25146 // --------- FIXME     
25147     onAdd : function(ds, records, index)
25148     {
25149         //Roo.log(['on Add', ds, records, index] );        
25150         this.clearSelections();
25151         if(this.nodes.length == 0){
25152             this.refresh();
25153             return;
25154         }
25155         var n = this.nodes[index];
25156         for(var i = 0, len = records.length; i < len; i++){
25157             var d = this.prepareData(records[i].data, i, records[i]);
25158             if(n){
25159                 this.tpl.insertBefore(n, d);
25160             }else{
25161                 
25162                 this.tpl.append(this.el, d);
25163             }
25164         }
25165         this.updateIndexes(index);
25166     },
25167
25168     onRemove : function(ds, record, index){
25169        // Roo.log('onRemove');
25170         this.clearSelections();
25171         var el = this.dataName  ?
25172             this.el.child('.roo-tpl-' + this.dataName) :
25173             this.el; 
25174         
25175         el.dom.removeChild(this.nodes[index]);
25176         this.updateIndexes(index);
25177     },
25178
25179     /**
25180      * Refresh an individual node.
25181      * @param {Number} index
25182      */
25183     refreshNode : function(index){
25184         this.onUpdate(this.store, this.store.getAt(index));
25185     },
25186
25187     updateIndexes : function(startIndex, endIndex){
25188         var ns = this.nodes;
25189         startIndex = startIndex || 0;
25190         endIndex = endIndex || ns.length - 1;
25191         for(var i = startIndex; i <= endIndex; i++){
25192             ns[i].nodeIndex = i;
25193         }
25194     },
25195
25196     /**
25197      * Changes the data store this view uses and refresh the view.
25198      * @param {Store} store
25199      */
25200     setStore : function(store, initial){
25201         if(!initial && this.store){
25202             this.store.un("datachanged", this.refresh);
25203             this.store.un("add", this.onAdd);
25204             this.store.un("remove", this.onRemove);
25205             this.store.un("update", this.onUpdate);
25206             this.store.un("clear", this.refresh);
25207             this.store.un("beforeload", this.onBeforeLoad);
25208             this.store.un("load", this.onLoad);
25209             this.store.un("loadexception", this.onLoad);
25210         }
25211         if(store){
25212           
25213             store.on("datachanged", this.refresh, this);
25214             store.on("add", this.onAdd, this);
25215             store.on("remove", this.onRemove, this);
25216             store.on("update", this.onUpdate, this);
25217             store.on("clear", this.refresh, this);
25218             store.on("beforeload", this.onBeforeLoad, this);
25219             store.on("load", this.onLoad, this);
25220             store.on("loadexception", this.onLoad, this);
25221         }
25222         
25223         if(store){
25224             this.refresh();
25225         }
25226     },
25227     /**
25228      * onbeforeLoad - masks the loading area.
25229      *
25230      */
25231     onBeforeLoad : function(store,opts)
25232     {
25233          //Roo.log('onBeforeLoad');   
25234         if (!opts.add) {
25235             this.el.update("");
25236         }
25237         this.el.mask(this.mask ? this.mask : "Loading" ); 
25238     },
25239     onLoad : function ()
25240     {
25241         this.el.unmask();
25242     },
25243     
25244
25245     /**
25246      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25247      * @param {HTMLElement} node
25248      * @return {HTMLElement} The template node
25249      */
25250     findItemFromChild : function(node){
25251         var el = this.dataName  ?
25252             this.el.child('.roo-tpl-' + this.dataName,true) :
25253             this.el.dom; 
25254         
25255         if(!node || node.parentNode == el){
25256                     return node;
25257             }
25258             var p = node.parentNode;
25259             while(p && p != el){
25260             if(p.parentNode == el){
25261                 return p;
25262             }
25263             p = p.parentNode;
25264         }
25265             return null;
25266     },
25267
25268     /** @ignore */
25269     onClick : function(e){
25270         var item = this.findItemFromChild(e.getTarget());
25271         if(item){
25272             var index = this.indexOf(item);
25273             if(this.onItemClick(item, index, e) !== false){
25274                 this.fireEvent("click", this, index, item, e);
25275             }
25276         }else{
25277             this.clearSelections();
25278         }
25279     },
25280
25281     /** @ignore */
25282     onContextMenu : function(e){
25283         var item = this.findItemFromChild(e.getTarget());
25284         if(item){
25285             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25286         }
25287     },
25288
25289     /** @ignore */
25290     onDblClick : function(e){
25291         var item = this.findItemFromChild(e.getTarget());
25292         if(item){
25293             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25294         }
25295     },
25296
25297     onItemClick : function(item, index, e)
25298     {
25299         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25300             return false;
25301         }
25302         if (this.toggleSelect) {
25303             var m = this.isSelected(item) ? 'unselect' : 'select';
25304             //Roo.log(m);
25305             var _t = this;
25306             _t[m](item, true, false);
25307             return true;
25308         }
25309         if(this.multiSelect || this.singleSelect){
25310             if(this.multiSelect && e.shiftKey && this.lastSelection){
25311                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25312             }else{
25313                 this.select(item, this.multiSelect && e.ctrlKey);
25314                 this.lastSelection = item;
25315             }
25316             
25317             if(!this.tickable){
25318                 e.preventDefault();
25319             }
25320             
25321         }
25322         return true;
25323     },
25324
25325     /**
25326      * Get the number of selected nodes.
25327      * @return {Number}
25328      */
25329     getSelectionCount : function(){
25330         return this.selections.length;
25331     },
25332
25333     /**
25334      * Get the currently selected nodes.
25335      * @return {Array} An array of HTMLElements
25336      */
25337     getSelectedNodes : function(){
25338         return this.selections;
25339     },
25340
25341     /**
25342      * Get the indexes of the selected nodes.
25343      * @return {Array}
25344      */
25345     getSelectedIndexes : function(){
25346         var indexes = [], s = this.selections;
25347         for(var i = 0, len = s.length; i < len; i++){
25348             indexes.push(s[i].nodeIndex);
25349         }
25350         return indexes;
25351     },
25352
25353     /**
25354      * Clear all selections
25355      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25356      */
25357     clearSelections : function(suppressEvent){
25358         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25359             this.cmp.elements = this.selections;
25360             this.cmp.removeClass(this.selectedClass);
25361             this.selections = [];
25362             if(!suppressEvent){
25363                 this.fireEvent("selectionchange", this, this.selections);
25364             }
25365         }
25366     },
25367
25368     /**
25369      * Returns true if the passed node is selected
25370      * @param {HTMLElement/Number} node The node or node index
25371      * @return {Boolean}
25372      */
25373     isSelected : function(node){
25374         var s = this.selections;
25375         if(s.length < 1){
25376             return false;
25377         }
25378         node = this.getNode(node);
25379         return s.indexOf(node) !== -1;
25380     },
25381
25382     /**
25383      * Selects nodes.
25384      * @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
25385      * @param {Boolean} keepExisting (optional) true to keep existing selections
25386      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25387      */
25388     select : function(nodeInfo, keepExisting, suppressEvent){
25389         if(nodeInfo instanceof Array){
25390             if(!keepExisting){
25391                 this.clearSelections(true);
25392             }
25393             for(var i = 0, len = nodeInfo.length; i < len; i++){
25394                 this.select(nodeInfo[i], true, true);
25395             }
25396             return;
25397         } 
25398         var node = this.getNode(nodeInfo);
25399         if(!node || this.isSelected(node)){
25400             return; // already selected.
25401         }
25402         if(!keepExisting){
25403             this.clearSelections(true);
25404         }
25405         
25406         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25407             Roo.fly(node).addClass(this.selectedClass);
25408             this.selections.push(node);
25409             if(!suppressEvent){
25410                 this.fireEvent("selectionchange", this, this.selections);
25411             }
25412         }
25413         
25414         
25415     },
25416       /**
25417      * Unselects nodes.
25418      * @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
25419      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25420      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25421      */
25422     unselect : function(nodeInfo, keepExisting, suppressEvent)
25423     {
25424         if(nodeInfo instanceof Array){
25425             Roo.each(this.selections, function(s) {
25426                 this.unselect(s, nodeInfo);
25427             }, this);
25428             return;
25429         }
25430         var node = this.getNode(nodeInfo);
25431         if(!node || !this.isSelected(node)){
25432             //Roo.log("not selected");
25433             return; // not selected.
25434         }
25435         // fireevent???
25436         var ns = [];
25437         Roo.each(this.selections, function(s) {
25438             if (s == node ) {
25439                 Roo.fly(node).removeClass(this.selectedClass);
25440
25441                 return;
25442             }
25443             ns.push(s);
25444         },this);
25445         
25446         this.selections= ns;
25447         this.fireEvent("selectionchange", this, this.selections);
25448     },
25449
25450     /**
25451      * Gets a template node.
25452      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25453      * @return {HTMLElement} The node or null if it wasn't found
25454      */
25455     getNode : function(nodeInfo){
25456         if(typeof nodeInfo == "string"){
25457             return document.getElementById(nodeInfo);
25458         }else if(typeof nodeInfo == "number"){
25459             return this.nodes[nodeInfo];
25460         }
25461         return nodeInfo;
25462     },
25463
25464     /**
25465      * Gets a range template nodes.
25466      * @param {Number} startIndex
25467      * @param {Number} endIndex
25468      * @return {Array} An array of nodes
25469      */
25470     getNodes : function(start, end){
25471         var ns = this.nodes;
25472         start = start || 0;
25473         end = typeof end == "undefined" ? ns.length - 1 : end;
25474         var nodes = [];
25475         if(start <= end){
25476             for(var i = start; i <= end; i++){
25477                 nodes.push(ns[i]);
25478             }
25479         } else{
25480             for(var i = start; i >= end; i--){
25481                 nodes.push(ns[i]);
25482             }
25483         }
25484         return nodes;
25485     },
25486
25487     /**
25488      * Finds the index of the passed node
25489      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25490      * @return {Number} The index of the node or -1
25491      */
25492     indexOf : function(node){
25493         node = this.getNode(node);
25494         if(typeof node.nodeIndex == "number"){
25495             return node.nodeIndex;
25496         }
25497         var ns = this.nodes;
25498         for(var i = 0, len = ns.length; i < len; i++){
25499             if(ns[i] == node){
25500                 return i;
25501             }
25502         }
25503         return -1;
25504     }
25505 });
25506 /*
25507  * Based on:
25508  * Ext JS Library 1.1.1
25509  * Copyright(c) 2006-2007, Ext JS, LLC.
25510  *
25511  * Originally Released Under LGPL - original licence link has changed is not relivant.
25512  *
25513  * Fork - LGPL
25514  * <script type="text/javascript">
25515  */
25516
25517 /**
25518  * @class Roo.JsonView
25519  * @extends Roo.View
25520  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25521 <pre><code>
25522 var view = new Roo.JsonView({
25523     container: "my-element",
25524     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25525     multiSelect: true, 
25526     jsonRoot: "data" 
25527 });
25528
25529 // listen for node click?
25530 view.on("click", function(vw, index, node, e){
25531     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25532 });
25533
25534 // direct load of JSON data
25535 view.load("foobar.php");
25536
25537 // Example from my blog list
25538 var tpl = new Roo.Template(
25539     '&lt;div class="entry"&gt;' +
25540     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25541     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25542     "&lt;/div&gt;&lt;hr /&gt;"
25543 );
25544
25545 var moreView = new Roo.JsonView({
25546     container :  "entry-list", 
25547     template : tpl,
25548     jsonRoot: "posts"
25549 });
25550 moreView.on("beforerender", this.sortEntries, this);
25551 moreView.load({
25552     url: "/blog/get-posts.php",
25553     params: "allposts=true",
25554     text: "Loading Blog Entries..."
25555 });
25556 </code></pre>
25557
25558 * Note: old code is supported with arguments : (container, template, config)
25559
25560
25561  * @constructor
25562  * Create a new JsonView
25563  * 
25564  * @param {Object} config The config object
25565  * 
25566  */
25567 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25568     
25569     
25570     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25571
25572     var um = this.el.getUpdateManager();
25573     um.setRenderer(this);
25574     um.on("update", this.onLoad, this);
25575     um.on("failure", this.onLoadException, this);
25576
25577     /**
25578      * @event beforerender
25579      * Fires before rendering of the downloaded JSON data.
25580      * @param {Roo.JsonView} this
25581      * @param {Object} data The JSON data loaded
25582      */
25583     /**
25584      * @event load
25585      * Fires when data is loaded.
25586      * @param {Roo.JsonView} this
25587      * @param {Object} data The JSON data loaded
25588      * @param {Object} response The raw Connect response object
25589      */
25590     /**
25591      * @event loadexception
25592      * Fires when loading fails.
25593      * @param {Roo.JsonView} this
25594      * @param {Object} response The raw Connect response object
25595      */
25596     this.addEvents({
25597         'beforerender' : true,
25598         'load' : true,
25599         'loadexception' : true
25600     });
25601 };
25602 Roo.extend(Roo.JsonView, Roo.View, {
25603     /**
25604      * @type {String} The root property in the loaded JSON object that contains the data
25605      */
25606     jsonRoot : "",
25607
25608     /**
25609      * Refreshes the view.
25610      */
25611     refresh : function(){
25612         this.clearSelections();
25613         this.el.update("");
25614         var html = [];
25615         var o = this.jsonData;
25616         if(o && o.length > 0){
25617             for(var i = 0, len = o.length; i < len; i++){
25618                 var data = this.prepareData(o[i], i, o);
25619                 html[html.length] = this.tpl.apply(data);
25620             }
25621         }else{
25622             html.push(this.emptyText);
25623         }
25624         this.el.update(html.join(""));
25625         this.nodes = this.el.dom.childNodes;
25626         this.updateIndexes(0);
25627     },
25628
25629     /**
25630      * 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.
25631      * @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:
25632      <pre><code>
25633      view.load({
25634          url: "your-url.php",
25635          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25636          callback: yourFunction,
25637          scope: yourObject, //(optional scope)
25638          discardUrl: false,
25639          nocache: false,
25640          text: "Loading...",
25641          timeout: 30,
25642          scripts: false
25643      });
25644      </code></pre>
25645      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25646      * 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.
25647      * @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}
25648      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25649      * @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.
25650      */
25651     load : function(){
25652         var um = this.el.getUpdateManager();
25653         um.update.apply(um, arguments);
25654     },
25655
25656     render : function(el, response){
25657         this.clearSelections();
25658         this.el.update("");
25659         var o;
25660         try{
25661             o = Roo.util.JSON.decode(response.responseText);
25662             if(this.jsonRoot){
25663                 
25664                 o = o[this.jsonRoot];
25665             }
25666         } catch(e){
25667         }
25668         /**
25669          * The current JSON data or null
25670          */
25671         this.jsonData = o;
25672         this.beforeRender();
25673         this.refresh();
25674     },
25675
25676 /**
25677  * Get the number of records in the current JSON dataset
25678  * @return {Number}
25679  */
25680     getCount : function(){
25681         return this.jsonData ? this.jsonData.length : 0;
25682     },
25683
25684 /**
25685  * Returns the JSON object for the specified node(s)
25686  * @param {HTMLElement/Array} node The node or an array of nodes
25687  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25688  * you get the JSON object for the node
25689  */
25690     getNodeData : function(node){
25691         if(node instanceof Array){
25692             var data = [];
25693             for(var i = 0, len = node.length; i < len; i++){
25694                 data.push(this.getNodeData(node[i]));
25695             }
25696             return data;
25697         }
25698         return this.jsonData[this.indexOf(node)] || null;
25699     },
25700
25701     beforeRender : function(){
25702         this.snapshot = this.jsonData;
25703         if(this.sortInfo){
25704             this.sort.apply(this, this.sortInfo);
25705         }
25706         this.fireEvent("beforerender", this, this.jsonData);
25707     },
25708
25709     onLoad : function(el, o){
25710         this.fireEvent("load", this, this.jsonData, o);
25711     },
25712
25713     onLoadException : function(el, o){
25714         this.fireEvent("loadexception", this, o);
25715     },
25716
25717 /**
25718  * Filter the data by a specific property.
25719  * @param {String} property A property on your JSON objects
25720  * @param {String/RegExp} value Either string that the property values
25721  * should start with, or a RegExp to test against the property
25722  */
25723     filter : function(property, value){
25724         if(this.jsonData){
25725             var data = [];
25726             var ss = this.snapshot;
25727             if(typeof value == "string"){
25728                 var vlen = value.length;
25729                 if(vlen == 0){
25730                     this.clearFilter();
25731                     return;
25732                 }
25733                 value = value.toLowerCase();
25734                 for(var i = 0, len = ss.length; i < len; i++){
25735                     var o = ss[i];
25736                     if(o[property].substr(0, vlen).toLowerCase() == value){
25737                         data.push(o);
25738                     }
25739                 }
25740             } else if(value.exec){ // regex?
25741                 for(var i = 0, len = ss.length; i < len; i++){
25742                     var o = ss[i];
25743                     if(value.test(o[property])){
25744                         data.push(o);
25745                     }
25746                 }
25747             } else{
25748                 return;
25749             }
25750             this.jsonData = data;
25751             this.refresh();
25752         }
25753     },
25754
25755 /**
25756  * Filter by a function. The passed function will be called with each
25757  * object in the current dataset. If the function returns true the value is kept,
25758  * otherwise it is filtered.
25759  * @param {Function} fn
25760  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25761  */
25762     filterBy : function(fn, scope){
25763         if(this.jsonData){
25764             var data = [];
25765             var ss = this.snapshot;
25766             for(var i = 0, len = ss.length; i < len; i++){
25767                 var o = ss[i];
25768                 if(fn.call(scope || this, o)){
25769                     data.push(o);
25770                 }
25771             }
25772             this.jsonData = data;
25773             this.refresh();
25774         }
25775     },
25776
25777 /**
25778  * Clears the current filter.
25779  */
25780     clearFilter : function(){
25781         if(this.snapshot && this.jsonData != this.snapshot){
25782             this.jsonData = this.snapshot;
25783             this.refresh();
25784         }
25785     },
25786
25787
25788 /**
25789  * Sorts the data for this view and refreshes it.
25790  * @param {String} property A property on your JSON objects to sort on
25791  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25792  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25793  */
25794     sort : function(property, dir, sortType){
25795         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25796         if(this.jsonData){
25797             var p = property;
25798             var dsc = dir && dir.toLowerCase() == "desc";
25799             var f = function(o1, o2){
25800                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25801                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25802                 ;
25803                 if(v1 < v2){
25804                     return dsc ? +1 : -1;
25805                 } else if(v1 > v2){
25806                     return dsc ? -1 : +1;
25807                 } else{
25808                     return 0;
25809                 }
25810             };
25811             this.jsonData.sort(f);
25812             this.refresh();
25813             if(this.jsonData != this.snapshot){
25814                 this.snapshot.sort(f);
25815             }
25816         }
25817     }
25818 });/*
25819  * Based on:
25820  * Ext JS Library 1.1.1
25821  * Copyright(c) 2006-2007, Ext JS, LLC.
25822  *
25823  * Originally Released Under LGPL - original licence link has changed is not relivant.
25824  *
25825  * Fork - LGPL
25826  * <script type="text/javascript">
25827  */
25828  
25829
25830 /**
25831  * @class Roo.ColorPalette
25832  * @extends Roo.Component
25833  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25834  * Here's an example of typical usage:
25835  * <pre><code>
25836 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25837 cp.render('my-div');
25838
25839 cp.on('select', function(palette, selColor){
25840     // do something with selColor
25841 });
25842 </code></pre>
25843  * @constructor
25844  * Create a new ColorPalette
25845  * @param {Object} config The config object
25846  */
25847 Roo.ColorPalette = function(config){
25848     Roo.ColorPalette.superclass.constructor.call(this, config);
25849     this.addEvents({
25850         /**
25851              * @event select
25852              * Fires when a color is selected
25853              * @param {ColorPalette} this
25854              * @param {String} color The 6-digit color hex code (without the # symbol)
25855              */
25856         select: true
25857     });
25858
25859     if(this.handler){
25860         this.on("select", this.handler, this.scope, true);
25861     }
25862 };
25863 Roo.extend(Roo.ColorPalette, Roo.Component, {
25864     /**
25865      * @cfg {String} itemCls
25866      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25867      */
25868     itemCls : "x-color-palette",
25869     /**
25870      * @cfg {String} value
25871      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25872      * the hex codes are case-sensitive.
25873      */
25874     value : null,
25875     clickEvent:'click',
25876     // private
25877     ctype: "Roo.ColorPalette",
25878
25879     /**
25880      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25881      */
25882     allowReselect : false,
25883
25884     /**
25885      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25886      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25887      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25888      * of colors with the width setting until the box is symmetrical.</p>
25889      * <p>You can override individual colors if needed:</p>
25890      * <pre><code>
25891 var cp = new Roo.ColorPalette();
25892 cp.colors[0] = "FF0000";  // change the first box to red
25893 </code></pre>
25894
25895 Or you can provide a custom array of your own for complete control:
25896 <pre><code>
25897 var cp = new Roo.ColorPalette();
25898 cp.colors = ["000000", "993300", "333300"];
25899 </code></pre>
25900      * @type Array
25901      */
25902     colors : [
25903         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25904         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25905         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25906         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25907         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25908     ],
25909
25910     // private
25911     onRender : function(container, position){
25912         var t = new Roo.MasterTemplate(
25913             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25914         );
25915         var c = this.colors;
25916         for(var i = 0, len = c.length; i < len; i++){
25917             t.add([c[i]]);
25918         }
25919         var el = document.createElement("div");
25920         el.className = this.itemCls;
25921         t.overwrite(el);
25922         container.dom.insertBefore(el, position);
25923         this.el = Roo.get(el);
25924         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25925         if(this.clickEvent != 'click'){
25926             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25927         }
25928     },
25929
25930     // private
25931     afterRender : function(){
25932         Roo.ColorPalette.superclass.afterRender.call(this);
25933         if(this.value){
25934             var s = this.value;
25935             this.value = null;
25936             this.select(s);
25937         }
25938     },
25939
25940     // private
25941     handleClick : function(e, t){
25942         e.preventDefault();
25943         if(!this.disabled){
25944             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25945             this.select(c.toUpperCase());
25946         }
25947     },
25948
25949     /**
25950      * Selects the specified color in the palette (fires the select event)
25951      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25952      */
25953     select : function(color){
25954         color = color.replace("#", "");
25955         if(color != this.value || this.allowReselect){
25956             var el = this.el;
25957             if(this.value){
25958                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25959             }
25960             el.child("a.color-"+color).addClass("x-color-palette-sel");
25961             this.value = color;
25962             this.fireEvent("select", this, color);
25963         }
25964     }
25965 });/*
25966  * Based on:
25967  * Ext JS Library 1.1.1
25968  * Copyright(c) 2006-2007, Ext JS, LLC.
25969  *
25970  * Originally Released Under LGPL - original licence link has changed is not relivant.
25971  *
25972  * Fork - LGPL
25973  * <script type="text/javascript">
25974  */
25975  
25976 /**
25977  * @class Roo.DatePicker
25978  * @extends Roo.Component
25979  * Simple date picker class.
25980  * @constructor
25981  * Create a new DatePicker
25982  * @param {Object} config The config object
25983  */
25984 Roo.DatePicker = function(config){
25985     Roo.DatePicker.superclass.constructor.call(this, config);
25986
25987     this.value = config && config.value ?
25988                  config.value.clearTime() : new Date().clearTime();
25989
25990     this.addEvents({
25991         /**
25992              * @event select
25993              * Fires when a date is selected
25994              * @param {DatePicker} this
25995              * @param {Date} date The selected date
25996              */
25997         'select': true,
25998         /**
25999              * @event monthchange
26000              * Fires when the displayed month changes 
26001              * @param {DatePicker} this
26002              * @param {Date} date The selected month
26003              */
26004         'monthchange': true
26005     });
26006
26007     if(this.handler){
26008         this.on("select", this.handler,  this.scope || this);
26009     }
26010     // build the disabledDatesRE
26011     if(!this.disabledDatesRE && this.disabledDates){
26012         var dd = this.disabledDates;
26013         var re = "(?:";
26014         for(var i = 0; i < dd.length; i++){
26015             re += dd[i];
26016             if(i != dd.length-1) re += "|";
26017         }
26018         this.disabledDatesRE = new RegExp(re + ")");
26019     }
26020 };
26021
26022 Roo.extend(Roo.DatePicker, Roo.Component, {
26023     /**
26024      * @cfg {String} todayText
26025      * The text to display on the button that selects the current date (defaults to "Today")
26026      */
26027     todayText : "Today",
26028     /**
26029      * @cfg {String} okText
26030      * The text to display on the ok button
26031      */
26032     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26033     /**
26034      * @cfg {String} cancelText
26035      * The text to display on the cancel button
26036      */
26037     cancelText : "Cancel",
26038     /**
26039      * @cfg {String} todayTip
26040      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26041      */
26042     todayTip : "{0} (Spacebar)",
26043     /**
26044      * @cfg {Date} minDate
26045      * Minimum allowable date (JavaScript date object, defaults to null)
26046      */
26047     minDate : null,
26048     /**
26049      * @cfg {Date} maxDate
26050      * Maximum allowable date (JavaScript date object, defaults to null)
26051      */
26052     maxDate : null,
26053     /**
26054      * @cfg {String} minText
26055      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26056      */
26057     minText : "This date is before the minimum date",
26058     /**
26059      * @cfg {String} maxText
26060      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26061      */
26062     maxText : "This date is after the maximum date",
26063     /**
26064      * @cfg {String} format
26065      * The default date format string which can be overriden for localization support.  The format must be
26066      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26067      */
26068     format : "m/d/y",
26069     /**
26070      * @cfg {Array} disabledDays
26071      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26072      */
26073     disabledDays : null,
26074     /**
26075      * @cfg {String} disabledDaysText
26076      * The tooltip to display when the date falls on a disabled day (defaults to "")
26077      */
26078     disabledDaysText : "",
26079     /**
26080      * @cfg {RegExp} disabledDatesRE
26081      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26082      */
26083     disabledDatesRE : null,
26084     /**
26085      * @cfg {String} disabledDatesText
26086      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26087      */
26088     disabledDatesText : "",
26089     /**
26090      * @cfg {Boolean} constrainToViewport
26091      * True to constrain the date picker to the viewport (defaults to true)
26092      */
26093     constrainToViewport : true,
26094     /**
26095      * @cfg {Array} monthNames
26096      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26097      */
26098     monthNames : Date.monthNames,
26099     /**
26100      * @cfg {Array} dayNames
26101      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26102      */
26103     dayNames : Date.dayNames,
26104     /**
26105      * @cfg {String} nextText
26106      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26107      */
26108     nextText: 'Next Month (Control+Right)',
26109     /**
26110      * @cfg {String} prevText
26111      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26112      */
26113     prevText: 'Previous Month (Control+Left)',
26114     /**
26115      * @cfg {String} monthYearText
26116      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26117      */
26118     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26119     /**
26120      * @cfg {Number} startDay
26121      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26122      */
26123     startDay : 0,
26124     /**
26125      * @cfg {Bool} showClear
26126      * Show a clear button (usefull for date form elements that can be blank.)
26127      */
26128     
26129     showClear: false,
26130     
26131     /**
26132      * Sets the value of the date field
26133      * @param {Date} value The date to set
26134      */
26135     setValue : function(value){
26136         var old = this.value;
26137         
26138         if (typeof(value) == 'string') {
26139          
26140             value = Date.parseDate(value, this.format);
26141         }
26142         if (!value) {
26143             value = new Date();
26144         }
26145         
26146         this.value = value.clearTime(true);
26147         if(this.el){
26148             this.update(this.value);
26149         }
26150     },
26151
26152     /**
26153      * Gets the current selected value of the date field
26154      * @return {Date} The selected date
26155      */
26156     getValue : function(){
26157         return this.value;
26158     },
26159
26160     // private
26161     focus : function(){
26162         if(this.el){
26163             this.update(this.activeDate);
26164         }
26165     },
26166
26167     // privateval
26168     onRender : function(container, position){
26169         
26170         var m = [
26171              '<table cellspacing="0">',
26172                 '<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>',
26173                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26174         var dn = this.dayNames;
26175         for(var i = 0; i < 7; i++){
26176             var d = this.startDay+i;
26177             if(d > 6){
26178                 d = d-7;
26179             }
26180             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26181         }
26182         m[m.length] = "</tr></thead><tbody><tr>";
26183         for(var i = 0; i < 42; i++) {
26184             if(i % 7 == 0 && i != 0){
26185                 m[m.length] = "</tr><tr>";
26186             }
26187             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26188         }
26189         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26190             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26191
26192         var el = document.createElement("div");
26193         el.className = "x-date-picker";
26194         el.innerHTML = m.join("");
26195
26196         container.dom.insertBefore(el, position);
26197
26198         this.el = Roo.get(el);
26199         this.eventEl = Roo.get(el.firstChild);
26200
26201         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26202             handler: this.showPrevMonth,
26203             scope: this,
26204             preventDefault:true,
26205             stopDefault:true
26206         });
26207
26208         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26209             handler: this.showNextMonth,
26210             scope: this,
26211             preventDefault:true,
26212             stopDefault:true
26213         });
26214
26215         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26216
26217         this.monthPicker = this.el.down('div.x-date-mp');
26218         this.monthPicker.enableDisplayMode('block');
26219         
26220         var kn = new Roo.KeyNav(this.eventEl, {
26221             "left" : function(e){
26222                 e.ctrlKey ?
26223                     this.showPrevMonth() :
26224                     this.update(this.activeDate.add("d", -1));
26225             },
26226
26227             "right" : function(e){
26228                 e.ctrlKey ?
26229                     this.showNextMonth() :
26230                     this.update(this.activeDate.add("d", 1));
26231             },
26232
26233             "up" : function(e){
26234                 e.ctrlKey ?
26235                     this.showNextYear() :
26236                     this.update(this.activeDate.add("d", -7));
26237             },
26238
26239             "down" : function(e){
26240                 e.ctrlKey ?
26241                     this.showPrevYear() :
26242                     this.update(this.activeDate.add("d", 7));
26243             },
26244
26245             "pageUp" : function(e){
26246                 this.showNextMonth();
26247             },
26248
26249             "pageDown" : function(e){
26250                 this.showPrevMonth();
26251             },
26252
26253             "enter" : function(e){
26254                 e.stopPropagation();
26255                 return true;
26256             },
26257
26258             scope : this
26259         });
26260
26261         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26262
26263         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26264
26265         this.el.unselectable();
26266         
26267         this.cells = this.el.select("table.x-date-inner tbody td");
26268         this.textNodes = this.el.query("table.x-date-inner tbody span");
26269
26270         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26271             text: "&#160;",
26272             tooltip: this.monthYearText
26273         });
26274
26275         this.mbtn.on('click', this.showMonthPicker, this);
26276         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26277
26278
26279         var today = (new Date()).dateFormat(this.format);
26280         
26281         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26282         if (this.showClear) {
26283             baseTb.add( new Roo.Toolbar.Fill());
26284         }
26285         baseTb.add({
26286             text: String.format(this.todayText, today),
26287             tooltip: String.format(this.todayTip, today),
26288             handler: this.selectToday,
26289             scope: this
26290         });
26291         
26292         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26293             
26294         //});
26295         if (this.showClear) {
26296             
26297             baseTb.add( new Roo.Toolbar.Fill());
26298             baseTb.add({
26299                 text: '&#160;',
26300                 cls: 'x-btn-icon x-btn-clear',
26301                 handler: function() {
26302                     //this.value = '';
26303                     this.fireEvent("select", this, '');
26304                 },
26305                 scope: this
26306             });
26307         }
26308         
26309         
26310         if(Roo.isIE){
26311             this.el.repaint();
26312         }
26313         this.update(this.value);
26314     },
26315
26316     createMonthPicker : function(){
26317         if(!this.monthPicker.dom.firstChild){
26318             var buf = ['<table border="0" cellspacing="0">'];
26319             for(var i = 0; i < 6; i++){
26320                 buf.push(
26321                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26322                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26323                     i == 0 ?
26324                     '<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>' :
26325                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26326                 );
26327             }
26328             buf.push(
26329                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26330                     this.okText,
26331                     '</button><button type="button" class="x-date-mp-cancel">',
26332                     this.cancelText,
26333                     '</button></td></tr>',
26334                 '</table>'
26335             );
26336             this.monthPicker.update(buf.join(''));
26337             this.monthPicker.on('click', this.onMonthClick, this);
26338             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26339
26340             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26341             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26342
26343             this.mpMonths.each(function(m, a, i){
26344                 i += 1;
26345                 if((i%2) == 0){
26346                     m.dom.xmonth = 5 + Math.round(i * .5);
26347                 }else{
26348                     m.dom.xmonth = Math.round((i-1) * .5);
26349                 }
26350             });
26351         }
26352     },
26353
26354     showMonthPicker : function(){
26355         this.createMonthPicker();
26356         var size = this.el.getSize();
26357         this.monthPicker.setSize(size);
26358         this.monthPicker.child('table').setSize(size);
26359
26360         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26361         this.updateMPMonth(this.mpSelMonth);
26362         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26363         this.updateMPYear(this.mpSelYear);
26364
26365         this.monthPicker.slideIn('t', {duration:.2});
26366     },
26367
26368     updateMPYear : function(y){
26369         this.mpyear = y;
26370         var ys = this.mpYears.elements;
26371         for(var i = 1; i <= 10; i++){
26372             var td = ys[i-1], y2;
26373             if((i%2) == 0){
26374                 y2 = y + Math.round(i * .5);
26375                 td.firstChild.innerHTML = y2;
26376                 td.xyear = y2;
26377             }else{
26378                 y2 = y - (5-Math.round(i * .5));
26379                 td.firstChild.innerHTML = y2;
26380                 td.xyear = y2;
26381             }
26382             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26383         }
26384     },
26385
26386     updateMPMonth : function(sm){
26387         this.mpMonths.each(function(m, a, i){
26388             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26389         });
26390     },
26391
26392     selectMPMonth: function(m){
26393         
26394     },
26395
26396     onMonthClick : function(e, t){
26397         e.stopEvent();
26398         var el = new Roo.Element(t), pn;
26399         if(el.is('button.x-date-mp-cancel')){
26400             this.hideMonthPicker();
26401         }
26402         else if(el.is('button.x-date-mp-ok')){
26403             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26404             this.hideMonthPicker();
26405         }
26406         else if(pn = el.up('td.x-date-mp-month', 2)){
26407             this.mpMonths.removeClass('x-date-mp-sel');
26408             pn.addClass('x-date-mp-sel');
26409             this.mpSelMonth = pn.dom.xmonth;
26410         }
26411         else if(pn = el.up('td.x-date-mp-year', 2)){
26412             this.mpYears.removeClass('x-date-mp-sel');
26413             pn.addClass('x-date-mp-sel');
26414             this.mpSelYear = pn.dom.xyear;
26415         }
26416         else if(el.is('a.x-date-mp-prev')){
26417             this.updateMPYear(this.mpyear-10);
26418         }
26419         else if(el.is('a.x-date-mp-next')){
26420             this.updateMPYear(this.mpyear+10);
26421         }
26422     },
26423
26424     onMonthDblClick : function(e, t){
26425         e.stopEvent();
26426         var el = new Roo.Element(t), pn;
26427         if(pn = el.up('td.x-date-mp-month', 2)){
26428             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26429             this.hideMonthPicker();
26430         }
26431         else if(pn = el.up('td.x-date-mp-year', 2)){
26432             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26433             this.hideMonthPicker();
26434         }
26435     },
26436
26437     hideMonthPicker : function(disableAnim){
26438         if(this.monthPicker){
26439             if(disableAnim === true){
26440                 this.monthPicker.hide();
26441             }else{
26442                 this.monthPicker.slideOut('t', {duration:.2});
26443             }
26444         }
26445     },
26446
26447     // private
26448     showPrevMonth : function(e){
26449         this.update(this.activeDate.add("mo", -1));
26450     },
26451
26452     // private
26453     showNextMonth : function(e){
26454         this.update(this.activeDate.add("mo", 1));
26455     },
26456
26457     // private
26458     showPrevYear : function(){
26459         this.update(this.activeDate.add("y", -1));
26460     },
26461
26462     // private
26463     showNextYear : function(){
26464         this.update(this.activeDate.add("y", 1));
26465     },
26466
26467     // private
26468     handleMouseWheel : function(e){
26469         var delta = e.getWheelDelta();
26470         if(delta > 0){
26471             this.showPrevMonth();
26472             e.stopEvent();
26473         } else if(delta < 0){
26474             this.showNextMonth();
26475             e.stopEvent();
26476         }
26477     },
26478
26479     // private
26480     handleDateClick : function(e, t){
26481         e.stopEvent();
26482         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26483             this.setValue(new Date(t.dateValue));
26484             this.fireEvent("select", this, this.value);
26485         }
26486     },
26487
26488     // private
26489     selectToday : function(){
26490         this.setValue(new Date().clearTime());
26491         this.fireEvent("select", this, this.value);
26492     },
26493
26494     // private
26495     update : function(date)
26496     {
26497         var vd = this.activeDate;
26498         this.activeDate = date;
26499         if(vd && this.el){
26500             var t = date.getTime();
26501             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26502                 this.cells.removeClass("x-date-selected");
26503                 this.cells.each(function(c){
26504                    if(c.dom.firstChild.dateValue == t){
26505                        c.addClass("x-date-selected");
26506                        setTimeout(function(){
26507                             try{c.dom.firstChild.focus();}catch(e){}
26508                        }, 50);
26509                        return false;
26510                    }
26511                 });
26512                 return;
26513             }
26514         }
26515         
26516         var days = date.getDaysInMonth();
26517         var firstOfMonth = date.getFirstDateOfMonth();
26518         var startingPos = firstOfMonth.getDay()-this.startDay;
26519
26520         if(startingPos <= this.startDay){
26521             startingPos += 7;
26522         }
26523
26524         var pm = date.add("mo", -1);
26525         var prevStart = pm.getDaysInMonth()-startingPos;
26526
26527         var cells = this.cells.elements;
26528         var textEls = this.textNodes;
26529         days += startingPos;
26530
26531         // convert everything to numbers so it's fast
26532         var day = 86400000;
26533         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26534         var today = new Date().clearTime().getTime();
26535         var sel = date.clearTime().getTime();
26536         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26537         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26538         var ddMatch = this.disabledDatesRE;
26539         var ddText = this.disabledDatesText;
26540         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26541         var ddaysText = this.disabledDaysText;
26542         var format = this.format;
26543
26544         var setCellClass = function(cal, cell){
26545             cell.title = "";
26546             var t = d.getTime();
26547             cell.firstChild.dateValue = t;
26548             if(t == today){
26549                 cell.className += " x-date-today";
26550                 cell.title = cal.todayText;
26551             }
26552             if(t == sel){
26553                 cell.className += " x-date-selected";
26554                 setTimeout(function(){
26555                     try{cell.firstChild.focus();}catch(e){}
26556                 }, 50);
26557             }
26558             // disabling
26559             if(t < min) {
26560                 cell.className = " x-date-disabled";
26561                 cell.title = cal.minText;
26562                 return;
26563             }
26564             if(t > max) {
26565                 cell.className = " x-date-disabled";
26566                 cell.title = cal.maxText;
26567                 return;
26568             }
26569             if(ddays){
26570                 if(ddays.indexOf(d.getDay()) != -1){
26571                     cell.title = ddaysText;
26572                     cell.className = " x-date-disabled";
26573                 }
26574             }
26575             if(ddMatch && format){
26576                 var fvalue = d.dateFormat(format);
26577                 if(ddMatch.test(fvalue)){
26578                     cell.title = ddText.replace("%0", fvalue);
26579                     cell.className = " x-date-disabled";
26580                 }
26581             }
26582         };
26583
26584         var i = 0;
26585         for(; i < startingPos; i++) {
26586             textEls[i].innerHTML = (++prevStart);
26587             d.setDate(d.getDate()+1);
26588             cells[i].className = "x-date-prevday";
26589             setCellClass(this, cells[i]);
26590         }
26591         for(; i < days; i++){
26592             intDay = i - startingPos + 1;
26593             textEls[i].innerHTML = (intDay);
26594             d.setDate(d.getDate()+1);
26595             cells[i].className = "x-date-active";
26596             setCellClass(this, cells[i]);
26597         }
26598         var extraDays = 0;
26599         for(; i < 42; i++) {
26600              textEls[i].innerHTML = (++extraDays);
26601              d.setDate(d.getDate()+1);
26602              cells[i].className = "x-date-nextday";
26603              setCellClass(this, cells[i]);
26604         }
26605
26606         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26607         this.fireEvent('monthchange', this, date);
26608         
26609         if(!this.internalRender){
26610             var main = this.el.dom.firstChild;
26611             var w = main.offsetWidth;
26612             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26613             Roo.fly(main).setWidth(w);
26614             this.internalRender = true;
26615             // opera does not respect the auto grow header center column
26616             // then, after it gets a width opera refuses to recalculate
26617             // without a second pass
26618             if(Roo.isOpera && !this.secondPass){
26619                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26620                 this.secondPass = true;
26621                 this.update.defer(10, this, [date]);
26622             }
26623         }
26624         
26625         
26626     }
26627 });        /*
26628  * Based on:
26629  * Ext JS Library 1.1.1
26630  * Copyright(c) 2006-2007, Ext JS, LLC.
26631  *
26632  * Originally Released Under LGPL - original licence link has changed is not relivant.
26633  *
26634  * Fork - LGPL
26635  * <script type="text/javascript">
26636  */
26637 /**
26638  * @class Roo.TabPanel
26639  * @extends Roo.util.Observable
26640  * A lightweight tab container.
26641  * <br><br>
26642  * Usage:
26643  * <pre><code>
26644 // basic tabs 1, built from existing content
26645 var tabs = new Roo.TabPanel("tabs1");
26646 tabs.addTab("script", "View Script");
26647 tabs.addTab("markup", "View Markup");
26648 tabs.activate("script");
26649
26650 // more advanced tabs, built from javascript
26651 var jtabs = new Roo.TabPanel("jtabs");
26652 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26653
26654 // set up the UpdateManager
26655 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26656 var updater = tab2.getUpdateManager();
26657 updater.setDefaultUrl("ajax1.htm");
26658 tab2.on('activate', updater.refresh, updater, true);
26659
26660 // Use setUrl for Ajax loading
26661 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26662 tab3.setUrl("ajax2.htm", null, true);
26663
26664 // Disabled tab
26665 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26666 tab4.disable();
26667
26668 jtabs.activate("jtabs-1");
26669  * </code></pre>
26670  * @constructor
26671  * Create a new TabPanel.
26672  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26673  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26674  */
26675 Roo.TabPanel = function(container, config){
26676     /**
26677     * The container element for this TabPanel.
26678     * @type Roo.Element
26679     */
26680     this.el = Roo.get(container, true);
26681     if(config){
26682         if(typeof config == "boolean"){
26683             this.tabPosition = config ? "bottom" : "top";
26684         }else{
26685             Roo.apply(this, config);
26686         }
26687     }
26688     if(this.tabPosition == "bottom"){
26689         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26690         this.el.addClass("x-tabs-bottom");
26691     }
26692     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26693     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26694     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26695     if(Roo.isIE){
26696         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26697     }
26698     if(this.tabPosition != "bottom"){
26699         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26700          * @type Roo.Element
26701          */
26702         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26703         this.el.addClass("x-tabs-top");
26704     }
26705     this.items = [];
26706
26707     this.bodyEl.setStyle("position", "relative");
26708
26709     this.active = null;
26710     this.activateDelegate = this.activate.createDelegate(this);
26711
26712     this.addEvents({
26713         /**
26714          * @event tabchange
26715          * Fires when the active tab changes
26716          * @param {Roo.TabPanel} this
26717          * @param {Roo.TabPanelItem} activePanel The new active tab
26718          */
26719         "tabchange": true,
26720         /**
26721          * @event beforetabchange
26722          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26723          * @param {Roo.TabPanel} this
26724          * @param {Object} e Set cancel to true on this object to cancel the tab change
26725          * @param {Roo.TabPanelItem} tab The tab being changed to
26726          */
26727         "beforetabchange" : true
26728     });
26729
26730     Roo.EventManager.onWindowResize(this.onResize, this);
26731     this.cpad = this.el.getPadding("lr");
26732     this.hiddenCount = 0;
26733
26734
26735     // toolbar on the tabbar support...
26736     if (this.toolbar) {
26737         var tcfg = this.toolbar;
26738         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26739         this.toolbar = new Roo.Toolbar(tcfg);
26740         if (Roo.isSafari) {
26741             var tbl = tcfg.container.child('table', true);
26742             tbl.setAttribute('width', '100%');
26743         }
26744         
26745     }
26746    
26747
26748
26749     Roo.TabPanel.superclass.constructor.call(this);
26750 };
26751
26752 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26753     /*
26754      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26755      */
26756     tabPosition : "top",
26757     /*
26758      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26759      */
26760     currentTabWidth : 0,
26761     /*
26762      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26763      */
26764     minTabWidth : 40,
26765     /*
26766      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26767      */
26768     maxTabWidth : 250,
26769     /*
26770      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26771      */
26772     preferredTabWidth : 175,
26773     /*
26774      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26775      */
26776     resizeTabs : false,
26777     /*
26778      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26779      */
26780     monitorResize : true,
26781     /*
26782      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26783      */
26784     toolbar : false,
26785
26786     /**
26787      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26788      * @param {String} id The id of the div to use <b>or create</b>
26789      * @param {String} text The text for the tab
26790      * @param {String} content (optional) Content to put in the TabPanelItem body
26791      * @param {Boolean} closable (optional) True to create a close icon on the tab
26792      * @return {Roo.TabPanelItem} The created TabPanelItem
26793      */
26794     addTab : function(id, text, content, closable){
26795         var item = new Roo.TabPanelItem(this, id, text, closable);
26796         this.addTabItem(item);
26797         if(content){
26798             item.setContent(content);
26799         }
26800         return item;
26801     },
26802
26803     /**
26804      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26805      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26806      * @return {Roo.TabPanelItem}
26807      */
26808     getTab : function(id){
26809         return this.items[id];
26810     },
26811
26812     /**
26813      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26814      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26815      */
26816     hideTab : function(id){
26817         var t = this.items[id];
26818         if(!t.isHidden()){
26819            t.setHidden(true);
26820            this.hiddenCount++;
26821            this.autoSizeTabs();
26822         }
26823     },
26824
26825     /**
26826      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26827      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26828      */
26829     unhideTab : function(id){
26830         var t = this.items[id];
26831         if(t.isHidden()){
26832            t.setHidden(false);
26833            this.hiddenCount--;
26834            this.autoSizeTabs();
26835         }
26836     },
26837
26838     /**
26839      * Adds an existing {@link Roo.TabPanelItem}.
26840      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26841      */
26842     addTabItem : function(item){
26843         this.items[item.id] = item;
26844         this.items.push(item);
26845         if(this.resizeTabs){
26846            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26847            this.autoSizeTabs();
26848         }else{
26849             item.autoSize();
26850         }
26851     },
26852
26853     /**
26854      * Removes a {@link Roo.TabPanelItem}.
26855      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26856      */
26857     removeTab : function(id){
26858         var items = this.items;
26859         var tab = items[id];
26860         if(!tab) { return; }
26861         var index = items.indexOf(tab);
26862         if(this.active == tab && items.length > 1){
26863             var newTab = this.getNextAvailable(index);
26864             if(newTab) {
26865                 newTab.activate();
26866             }
26867         }
26868         this.stripEl.dom.removeChild(tab.pnode.dom);
26869         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26870             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26871         }
26872         items.splice(index, 1);
26873         delete this.items[tab.id];
26874         tab.fireEvent("close", tab);
26875         tab.purgeListeners();
26876         this.autoSizeTabs();
26877     },
26878
26879     getNextAvailable : function(start){
26880         var items = this.items;
26881         var index = start;
26882         // look for a next tab that will slide over to
26883         // replace the one being removed
26884         while(index < items.length){
26885             var item = items[++index];
26886             if(item && !item.isHidden()){
26887                 return item;
26888             }
26889         }
26890         // if one isn't found select the previous tab (on the left)
26891         index = start;
26892         while(index >= 0){
26893             var item = items[--index];
26894             if(item && !item.isHidden()){
26895                 return item;
26896             }
26897         }
26898         return null;
26899     },
26900
26901     /**
26902      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26903      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26904      */
26905     disableTab : function(id){
26906         var tab = this.items[id];
26907         if(tab && this.active != tab){
26908             tab.disable();
26909         }
26910     },
26911
26912     /**
26913      * Enables a {@link Roo.TabPanelItem} that is disabled.
26914      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26915      */
26916     enableTab : function(id){
26917         var tab = this.items[id];
26918         tab.enable();
26919     },
26920
26921     /**
26922      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26923      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26924      * @return {Roo.TabPanelItem} The TabPanelItem.
26925      */
26926     activate : function(id){
26927         var tab = this.items[id];
26928         if(!tab){
26929             return null;
26930         }
26931         if(tab == this.active || tab.disabled){
26932             return tab;
26933         }
26934         var e = {};
26935         this.fireEvent("beforetabchange", this, e, tab);
26936         if(e.cancel !== true && !tab.disabled){
26937             if(this.active){
26938                 this.active.hide();
26939             }
26940             this.active = this.items[id];
26941             this.active.show();
26942             this.fireEvent("tabchange", this, this.active);
26943         }
26944         return tab;
26945     },
26946
26947     /**
26948      * Gets the active {@link Roo.TabPanelItem}.
26949      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26950      */
26951     getActiveTab : function(){
26952         return this.active;
26953     },
26954
26955     /**
26956      * Updates the tab body element to fit the height of the container element
26957      * for overflow scrolling
26958      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26959      */
26960     syncHeight : function(targetHeight){
26961         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26962         var bm = this.bodyEl.getMargins();
26963         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26964         this.bodyEl.setHeight(newHeight);
26965         return newHeight;
26966     },
26967
26968     onResize : function(){
26969         if(this.monitorResize){
26970             this.autoSizeTabs();
26971         }
26972     },
26973
26974     /**
26975      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26976      */
26977     beginUpdate : function(){
26978         this.updating = true;
26979     },
26980
26981     /**
26982      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26983      */
26984     endUpdate : function(){
26985         this.updating = false;
26986         this.autoSizeTabs();
26987     },
26988
26989     /**
26990      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26991      */
26992     autoSizeTabs : function(){
26993         var count = this.items.length;
26994         var vcount = count - this.hiddenCount;
26995         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26996         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26997         var availWidth = Math.floor(w / vcount);
26998         var b = this.stripBody;
26999         if(b.getWidth() > w){
27000             var tabs = this.items;
27001             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27002             if(availWidth < this.minTabWidth){
27003                 /*if(!this.sleft){    // incomplete scrolling code
27004                     this.createScrollButtons();
27005                 }
27006                 this.showScroll();
27007                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27008             }
27009         }else{
27010             if(this.currentTabWidth < this.preferredTabWidth){
27011                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27012             }
27013         }
27014     },
27015
27016     /**
27017      * Returns the number of tabs in this TabPanel.
27018      * @return {Number}
27019      */
27020      getCount : function(){
27021          return this.items.length;
27022      },
27023
27024     /**
27025      * Resizes all the tabs to the passed width
27026      * @param {Number} The new width
27027      */
27028     setTabWidth : function(width){
27029         this.currentTabWidth = width;
27030         for(var i = 0, len = this.items.length; i < len; i++) {
27031                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27032         }
27033     },
27034
27035     /**
27036      * Destroys this TabPanel
27037      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27038      */
27039     destroy : function(removeEl){
27040         Roo.EventManager.removeResizeListener(this.onResize, this);
27041         for(var i = 0, len = this.items.length; i < len; i++){
27042             this.items[i].purgeListeners();
27043         }
27044         if(removeEl === true){
27045             this.el.update("");
27046             this.el.remove();
27047         }
27048     }
27049 });
27050
27051 /**
27052  * @class Roo.TabPanelItem
27053  * @extends Roo.util.Observable
27054  * Represents an individual item (tab plus body) in a TabPanel.
27055  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27056  * @param {String} id The id of this TabPanelItem
27057  * @param {String} text The text for the tab of this TabPanelItem
27058  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27059  */
27060 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27061     /**
27062      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27063      * @type Roo.TabPanel
27064      */
27065     this.tabPanel = tabPanel;
27066     /**
27067      * The id for this TabPanelItem
27068      * @type String
27069      */
27070     this.id = id;
27071     /** @private */
27072     this.disabled = false;
27073     /** @private */
27074     this.text = text;
27075     /** @private */
27076     this.loaded = false;
27077     this.closable = closable;
27078
27079     /**
27080      * The body element for this TabPanelItem.
27081      * @type Roo.Element
27082      */
27083     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27084     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27085     this.bodyEl.setStyle("display", "block");
27086     this.bodyEl.setStyle("zoom", "1");
27087     this.hideAction();
27088
27089     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27090     /** @private */
27091     this.el = Roo.get(els.el, true);
27092     this.inner = Roo.get(els.inner, true);
27093     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27094     this.pnode = Roo.get(els.el.parentNode, true);
27095     this.el.on("mousedown", this.onTabMouseDown, this);
27096     this.el.on("click", this.onTabClick, this);
27097     /** @private */
27098     if(closable){
27099         var c = Roo.get(els.close, true);
27100         c.dom.title = this.closeText;
27101         c.addClassOnOver("close-over");
27102         c.on("click", this.closeClick, this);
27103      }
27104
27105     this.addEvents({
27106          /**
27107          * @event activate
27108          * Fires when this tab becomes the active tab.
27109          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27110          * @param {Roo.TabPanelItem} this
27111          */
27112         "activate": true,
27113         /**
27114          * @event beforeclose
27115          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27116          * @param {Roo.TabPanelItem} this
27117          * @param {Object} e Set cancel to true on this object to cancel the close.
27118          */
27119         "beforeclose": true,
27120         /**
27121          * @event close
27122          * Fires when this tab is closed.
27123          * @param {Roo.TabPanelItem} this
27124          */
27125          "close": true,
27126         /**
27127          * @event deactivate
27128          * Fires when this tab is no longer the active tab.
27129          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27130          * @param {Roo.TabPanelItem} this
27131          */
27132          "deactivate" : true
27133     });
27134     this.hidden = false;
27135
27136     Roo.TabPanelItem.superclass.constructor.call(this);
27137 };
27138
27139 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27140     purgeListeners : function(){
27141        Roo.util.Observable.prototype.purgeListeners.call(this);
27142        this.el.removeAllListeners();
27143     },
27144     /**
27145      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27146      */
27147     show : function(){
27148         this.pnode.addClass("on");
27149         this.showAction();
27150         if(Roo.isOpera){
27151             this.tabPanel.stripWrap.repaint();
27152         }
27153         this.fireEvent("activate", this.tabPanel, this);
27154     },
27155
27156     /**
27157      * Returns true if this tab is the active tab.
27158      * @return {Boolean}
27159      */
27160     isActive : function(){
27161         return this.tabPanel.getActiveTab() == this;
27162     },
27163
27164     /**
27165      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27166      */
27167     hide : function(){
27168         this.pnode.removeClass("on");
27169         this.hideAction();
27170         this.fireEvent("deactivate", this.tabPanel, this);
27171     },
27172
27173     hideAction : function(){
27174         this.bodyEl.hide();
27175         this.bodyEl.setStyle("position", "absolute");
27176         this.bodyEl.setLeft("-20000px");
27177         this.bodyEl.setTop("-20000px");
27178     },
27179
27180     showAction : function(){
27181         this.bodyEl.setStyle("position", "relative");
27182         this.bodyEl.setTop("");
27183         this.bodyEl.setLeft("");
27184         this.bodyEl.show();
27185     },
27186
27187     /**
27188      * Set the tooltip for the tab.
27189      * @param {String} tooltip The tab's tooltip
27190      */
27191     setTooltip : function(text){
27192         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27193             this.textEl.dom.qtip = text;
27194             this.textEl.dom.removeAttribute('title');
27195         }else{
27196             this.textEl.dom.title = text;
27197         }
27198     },
27199
27200     onTabClick : function(e){
27201         e.preventDefault();
27202         this.tabPanel.activate(this.id);
27203     },
27204
27205     onTabMouseDown : function(e){
27206         e.preventDefault();
27207         this.tabPanel.activate(this.id);
27208     },
27209
27210     getWidth : function(){
27211         return this.inner.getWidth();
27212     },
27213
27214     setWidth : function(width){
27215         var iwidth = width - this.pnode.getPadding("lr");
27216         this.inner.setWidth(iwidth);
27217         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27218         this.pnode.setWidth(width);
27219     },
27220
27221     /**
27222      * Show or hide the tab
27223      * @param {Boolean} hidden True to hide or false to show.
27224      */
27225     setHidden : function(hidden){
27226         this.hidden = hidden;
27227         this.pnode.setStyle("display", hidden ? "none" : "");
27228     },
27229
27230     /**
27231      * Returns true if this tab is "hidden"
27232      * @return {Boolean}
27233      */
27234     isHidden : function(){
27235         return this.hidden;
27236     },
27237
27238     /**
27239      * Returns the text for this tab
27240      * @return {String}
27241      */
27242     getText : function(){
27243         return this.text;
27244     },
27245
27246     autoSize : function(){
27247         //this.el.beginMeasure();
27248         this.textEl.setWidth(1);
27249         /*
27250          *  #2804 [new] Tabs in Roojs
27251          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27252          */
27253         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27254         //this.el.endMeasure();
27255     },
27256
27257     /**
27258      * Sets the text for the tab (Note: this also sets the tooltip text)
27259      * @param {String} text The tab's text and tooltip
27260      */
27261     setText : function(text){
27262         this.text = text;
27263         this.textEl.update(text);
27264         this.setTooltip(text);
27265         if(!this.tabPanel.resizeTabs){
27266             this.autoSize();
27267         }
27268     },
27269     /**
27270      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27271      */
27272     activate : function(){
27273         this.tabPanel.activate(this.id);
27274     },
27275
27276     /**
27277      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27278      */
27279     disable : function(){
27280         if(this.tabPanel.active != this){
27281             this.disabled = true;
27282             this.pnode.addClass("disabled");
27283         }
27284     },
27285
27286     /**
27287      * Enables this TabPanelItem if it was previously disabled.
27288      */
27289     enable : function(){
27290         this.disabled = false;
27291         this.pnode.removeClass("disabled");
27292     },
27293
27294     /**
27295      * Sets the content for this TabPanelItem.
27296      * @param {String} content The content
27297      * @param {Boolean} loadScripts true to look for and load scripts
27298      */
27299     setContent : function(content, loadScripts){
27300         this.bodyEl.update(content, loadScripts);
27301     },
27302
27303     /**
27304      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27305      * @return {Roo.UpdateManager} The UpdateManager
27306      */
27307     getUpdateManager : function(){
27308         return this.bodyEl.getUpdateManager();
27309     },
27310
27311     /**
27312      * Set a URL to be used to load the content for this TabPanelItem.
27313      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27314      * @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)
27315      * @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)
27316      * @return {Roo.UpdateManager} The UpdateManager
27317      */
27318     setUrl : function(url, params, loadOnce){
27319         if(this.refreshDelegate){
27320             this.un('activate', this.refreshDelegate);
27321         }
27322         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27323         this.on("activate", this.refreshDelegate);
27324         return this.bodyEl.getUpdateManager();
27325     },
27326
27327     /** @private */
27328     _handleRefresh : function(url, params, loadOnce){
27329         if(!loadOnce || !this.loaded){
27330             var updater = this.bodyEl.getUpdateManager();
27331             updater.update(url, params, this._setLoaded.createDelegate(this));
27332         }
27333     },
27334
27335     /**
27336      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27337      *   Will fail silently if the setUrl method has not been called.
27338      *   This does not activate the panel, just updates its content.
27339      */
27340     refresh : function(){
27341         if(this.refreshDelegate){
27342            this.loaded = false;
27343            this.refreshDelegate();
27344         }
27345     },
27346
27347     /** @private */
27348     _setLoaded : function(){
27349         this.loaded = true;
27350     },
27351
27352     /** @private */
27353     closeClick : function(e){
27354         var o = {};
27355         e.stopEvent();
27356         this.fireEvent("beforeclose", this, o);
27357         if(o.cancel !== true){
27358             this.tabPanel.removeTab(this.id);
27359         }
27360     },
27361     /**
27362      * The text displayed in the tooltip for the close icon.
27363      * @type String
27364      */
27365     closeText : "Close this tab"
27366 });
27367
27368 /** @private */
27369 Roo.TabPanel.prototype.createStrip = function(container){
27370     var strip = document.createElement("div");
27371     strip.className = "x-tabs-wrap";
27372     container.appendChild(strip);
27373     return strip;
27374 };
27375 /** @private */
27376 Roo.TabPanel.prototype.createStripList = function(strip){
27377     // div wrapper for retard IE
27378     // returns the "tr" element.
27379     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27380         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27381         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27382     return strip.firstChild.firstChild.firstChild.firstChild;
27383 };
27384 /** @private */
27385 Roo.TabPanel.prototype.createBody = function(container){
27386     var body = document.createElement("div");
27387     Roo.id(body, "tab-body");
27388     Roo.fly(body).addClass("x-tabs-body");
27389     container.appendChild(body);
27390     return body;
27391 };
27392 /** @private */
27393 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27394     var body = Roo.getDom(id);
27395     if(!body){
27396         body = document.createElement("div");
27397         body.id = id;
27398     }
27399     Roo.fly(body).addClass("x-tabs-item-body");
27400     bodyEl.insertBefore(body, bodyEl.firstChild);
27401     return body;
27402 };
27403 /** @private */
27404 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27405     var td = document.createElement("td");
27406     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27407     //stripEl.appendChild(td);
27408     if(closable){
27409         td.className = "x-tabs-closable";
27410         if(!this.closeTpl){
27411             this.closeTpl = new Roo.Template(
27412                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27413                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27414                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27415             );
27416         }
27417         var el = this.closeTpl.overwrite(td, {"text": text});
27418         var close = el.getElementsByTagName("div")[0];
27419         var inner = el.getElementsByTagName("em")[0];
27420         return {"el": el, "close": close, "inner": inner};
27421     } else {
27422         if(!this.tabTpl){
27423             this.tabTpl = new Roo.Template(
27424                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27425                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27426             );
27427         }
27428         var el = this.tabTpl.overwrite(td, {"text": text});
27429         var inner = el.getElementsByTagName("em")[0];
27430         return {"el": el, "inner": inner};
27431     }
27432 };/*
27433  * Based on:
27434  * Ext JS Library 1.1.1
27435  * Copyright(c) 2006-2007, Ext JS, LLC.
27436  *
27437  * Originally Released Under LGPL - original licence link has changed is not relivant.
27438  *
27439  * Fork - LGPL
27440  * <script type="text/javascript">
27441  */
27442
27443 /**
27444  * @class Roo.Button
27445  * @extends Roo.util.Observable
27446  * Simple Button class
27447  * @cfg {String} text The button text
27448  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27449  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27450  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27451  * @cfg {Object} scope The scope of the handler
27452  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27453  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27454  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27455  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27456  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27457  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27458    applies if enableToggle = true)
27459  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27460  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27461   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27462  * @constructor
27463  * Create a new button
27464  * @param {Object} config The config object
27465  */
27466 Roo.Button = function(renderTo, config)
27467 {
27468     if (!config) {
27469         config = renderTo;
27470         renderTo = config.renderTo || false;
27471     }
27472     
27473     Roo.apply(this, config);
27474     this.addEvents({
27475         /**
27476              * @event click
27477              * Fires when this button is clicked
27478              * @param {Button} this
27479              * @param {EventObject} e The click event
27480              */
27481             "click" : true,
27482         /**
27483              * @event toggle
27484              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27485              * @param {Button} this
27486              * @param {Boolean} pressed
27487              */
27488             "toggle" : true,
27489         /**
27490              * @event mouseover
27491              * Fires when the mouse hovers over the button
27492              * @param {Button} this
27493              * @param {Event} e The event object
27494              */
27495         'mouseover' : true,
27496         /**
27497              * @event mouseout
27498              * Fires when the mouse exits the button
27499              * @param {Button} this
27500              * @param {Event} e The event object
27501              */
27502         'mouseout': true,
27503          /**
27504              * @event render
27505              * Fires when the button is rendered
27506              * @param {Button} this
27507              */
27508         'render': true
27509     });
27510     if(this.menu){
27511         this.menu = Roo.menu.MenuMgr.get(this.menu);
27512     }
27513     // register listeners first!!  - so render can be captured..
27514     Roo.util.Observable.call(this);
27515     if(renderTo){
27516         this.render(renderTo);
27517     }
27518     
27519   
27520 };
27521
27522 Roo.extend(Roo.Button, Roo.util.Observable, {
27523     /**
27524      * 
27525      */
27526     
27527     /**
27528      * Read-only. True if this button is hidden
27529      * @type Boolean
27530      */
27531     hidden : false,
27532     /**
27533      * Read-only. True if this button is disabled
27534      * @type Boolean
27535      */
27536     disabled : false,
27537     /**
27538      * Read-only. True if this button is pressed (only if enableToggle = true)
27539      * @type Boolean
27540      */
27541     pressed : false,
27542
27543     /**
27544      * @cfg {Number} tabIndex 
27545      * The DOM tabIndex for this button (defaults to undefined)
27546      */
27547     tabIndex : undefined,
27548
27549     /**
27550      * @cfg {Boolean} enableToggle
27551      * True to enable pressed/not pressed toggling (defaults to false)
27552      */
27553     enableToggle: false,
27554     /**
27555      * @cfg {Mixed} menu
27556      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27557      */
27558     menu : undefined,
27559     /**
27560      * @cfg {String} menuAlign
27561      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27562      */
27563     menuAlign : "tl-bl?",
27564
27565     /**
27566      * @cfg {String} iconCls
27567      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27568      */
27569     iconCls : undefined,
27570     /**
27571      * @cfg {String} type
27572      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27573      */
27574     type : 'button',
27575
27576     // private
27577     menuClassTarget: 'tr',
27578
27579     /**
27580      * @cfg {String} clickEvent
27581      * The type of event to map to the button's event handler (defaults to 'click')
27582      */
27583     clickEvent : 'click',
27584
27585     /**
27586      * @cfg {Boolean} handleMouseEvents
27587      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27588      */
27589     handleMouseEvents : true,
27590
27591     /**
27592      * @cfg {String} tooltipType
27593      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27594      */
27595     tooltipType : 'qtip',
27596
27597     /**
27598      * @cfg {String} cls
27599      * A CSS class to apply to the button's main element.
27600      */
27601     
27602     /**
27603      * @cfg {Roo.Template} template (Optional)
27604      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27605      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27606      * require code modifications if required elements (e.g. a button) aren't present.
27607      */
27608
27609     // private
27610     render : function(renderTo){
27611         var btn;
27612         if(this.hideParent){
27613             this.parentEl = Roo.get(renderTo);
27614         }
27615         if(!this.dhconfig){
27616             if(!this.template){
27617                 if(!Roo.Button.buttonTemplate){
27618                     // hideous table template
27619                     Roo.Button.buttonTemplate = new Roo.Template(
27620                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27621                         '<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>',
27622                         "</tr></tbody></table>");
27623                 }
27624                 this.template = Roo.Button.buttonTemplate;
27625             }
27626             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27627             var btnEl = btn.child("button:first");
27628             btnEl.on('focus', this.onFocus, this);
27629             btnEl.on('blur', this.onBlur, this);
27630             if(this.cls){
27631                 btn.addClass(this.cls);
27632             }
27633             if(this.icon){
27634                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27635             }
27636             if(this.iconCls){
27637                 btnEl.addClass(this.iconCls);
27638                 if(!this.cls){
27639                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27640                 }
27641             }
27642             if(this.tabIndex !== undefined){
27643                 btnEl.dom.tabIndex = this.tabIndex;
27644             }
27645             if(this.tooltip){
27646                 if(typeof this.tooltip == 'object'){
27647                     Roo.QuickTips.tips(Roo.apply({
27648                           target: btnEl.id
27649                     }, this.tooltip));
27650                 } else {
27651                     btnEl.dom[this.tooltipType] = this.tooltip;
27652                 }
27653             }
27654         }else{
27655             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27656         }
27657         this.el = btn;
27658         if(this.id){
27659             this.el.dom.id = this.el.id = this.id;
27660         }
27661         if(this.menu){
27662             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27663             this.menu.on("show", this.onMenuShow, this);
27664             this.menu.on("hide", this.onMenuHide, this);
27665         }
27666         btn.addClass("x-btn");
27667         if(Roo.isIE && !Roo.isIE7){
27668             this.autoWidth.defer(1, this);
27669         }else{
27670             this.autoWidth();
27671         }
27672         if(this.handleMouseEvents){
27673             btn.on("mouseover", this.onMouseOver, this);
27674             btn.on("mouseout", this.onMouseOut, this);
27675             btn.on("mousedown", this.onMouseDown, this);
27676         }
27677         btn.on(this.clickEvent, this.onClick, this);
27678         //btn.on("mouseup", this.onMouseUp, this);
27679         if(this.hidden){
27680             this.hide();
27681         }
27682         if(this.disabled){
27683             this.disable();
27684         }
27685         Roo.ButtonToggleMgr.register(this);
27686         if(this.pressed){
27687             this.el.addClass("x-btn-pressed");
27688         }
27689         if(this.repeat){
27690             var repeater = new Roo.util.ClickRepeater(btn,
27691                 typeof this.repeat == "object" ? this.repeat : {}
27692             );
27693             repeater.on("click", this.onClick,  this);
27694         }
27695         
27696         this.fireEvent('render', this);
27697         
27698     },
27699     /**
27700      * Returns the button's underlying element
27701      * @return {Roo.Element} The element
27702      */
27703     getEl : function(){
27704         return this.el;  
27705     },
27706     
27707     /**
27708      * Destroys this Button and removes any listeners.
27709      */
27710     destroy : function(){
27711         Roo.ButtonToggleMgr.unregister(this);
27712         this.el.removeAllListeners();
27713         this.purgeListeners();
27714         this.el.remove();
27715     },
27716
27717     // private
27718     autoWidth : function(){
27719         if(this.el){
27720             this.el.setWidth("auto");
27721             if(Roo.isIE7 && Roo.isStrict){
27722                 var ib = this.el.child('button');
27723                 if(ib && ib.getWidth() > 20){
27724                     ib.clip();
27725                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27726                 }
27727             }
27728             if(this.minWidth){
27729                 if(this.hidden){
27730                     this.el.beginMeasure();
27731                 }
27732                 if(this.el.getWidth() < this.minWidth){
27733                     this.el.setWidth(this.minWidth);
27734                 }
27735                 if(this.hidden){
27736                     this.el.endMeasure();
27737                 }
27738             }
27739         }
27740     },
27741
27742     /**
27743      * Assigns this button's click handler
27744      * @param {Function} handler The function to call when the button is clicked
27745      * @param {Object} scope (optional) Scope for the function passed in
27746      */
27747     setHandler : function(handler, scope){
27748         this.handler = handler;
27749         this.scope = scope;  
27750     },
27751     
27752     /**
27753      * Sets this button's text
27754      * @param {String} text The button text
27755      */
27756     setText : function(text){
27757         this.text = text;
27758         if(this.el){
27759             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27760         }
27761         this.autoWidth();
27762     },
27763     
27764     /**
27765      * Gets the text for this button
27766      * @return {String} The button text
27767      */
27768     getText : function(){
27769         return this.text;  
27770     },
27771     
27772     /**
27773      * Show this button
27774      */
27775     show: function(){
27776         this.hidden = false;
27777         if(this.el){
27778             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27779         }
27780     },
27781     
27782     /**
27783      * Hide this button
27784      */
27785     hide: function(){
27786         this.hidden = true;
27787         if(this.el){
27788             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27789         }
27790     },
27791     
27792     /**
27793      * Convenience function for boolean show/hide
27794      * @param {Boolean} visible True to show, false to hide
27795      */
27796     setVisible: function(visible){
27797         if(visible) {
27798             this.show();
27799         }else{
27800             this.hide();
27801         }
27802     },
27803     
27804     /**
27805      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27806      * @param {Boolean} state (optional) Force a particular state
27807      */
27808     toggle : function(state){
27809         state = state === undefined ? !this.pressed : state;
27810         if(state != this.pressed){
27811             if(state){
27812                 this.el.addClass("x-btn-pressed");
27813                 this.pressed = true;
27814                 this.fireEvent("toggle", this, true);
27815             }else{
27816                 this.el.removeClass("x-btn-pressed");
27817                 this.pressed = false;
27818                 this.fireEvent("toggle", this, false);
27819             }
27820             if(this.toggleHandler){
27821                 this.toggleHandler.call(this.scope || this, this, state);
27822             }
27823         }
27824     },
27825     
27826     /**
27827      * Focus the button
27828      */
27829     focus : function(){
27830         this.el.child('button:first').focus();
27831     },
27832     
27833     /**
27834      * Disable this button
27835      */
27836     disable : function(){
27837         if(this.el){
27838             this.el.addClass("x-btn-disabled");
27839         }
27840         this.disabled = true;
27841     },
27842     
27843     /**
27844      * Enable this button
27845      */
27846     enable : function(){
27847         if(this.el){
27848             this.el.removeClass("x-btn-disabled");
27849         }
27850         this.disabled = false;
27851     },
27852
27853     /**
27854      * Convenience function for boolean enable/disable
27855      * @param {Boolean} enabled True to enable, false to disable
27856      */
27857     setDisabled : function(v){
27858         this[v !== true ? "enable" : "disable"]();
27859     },
27860
27861     // private
27862     onClick : function(e)
27863     {
27864         if(e){
27865             e.preventDefault();
27866         }
27867         if(e.button != 0){
27868             return;
27869         }
27870         if(!this.disabled){
27871             if(this.enableToggle){
27872                 this.toggle();
27873             }
27874             if(this.menu && !this.menu.isVisible()){
27875                 this.menu.show(this.el, this.menuAlign);
27876             }
27877             this.fireEvent("click", this, e);
27878             if(this.handler){
27879                 this.el.removeClass("x-btn-over");
27880                 this.handler.call(this.scope || this, this, e);
27881             }
27882         }
27883     },
27884     // private
27885     onMouseOver : function(e){
27886         if(!this.disabled){
27887             this.el.addClass("x-btn-over");
27888             this.fireEvent('mouseover', this, e);
27889         }
27890     },
27891     // private
27892     onMouseOut : function(e){
27893         if(!e.within(this.el,  true)){
27894             this.el.removeClass("x-btn-over");
27895             this.fireEvent('mouseout', this, e);
27896         }
27897     },
27898     // private
27899     onFocus : function(e){
27900         if(!this.disabled){
27901             this.el.addClass("x-btn-focus");
27902         }
27903     },
27904     // private
27905     onBlur : function(e){
27906         this.el.removeClass("x-btn-focus");
27907     },
27908     // private
27909     onMouseDown : function(e){
27910         if(!this.disabled && e.button == 0){
27911             this.el.addClass("x-btn-click");
27912             Roo.get(document).on('mouseup', this.onMouseUp, this);
27913         }
27914     },
27915     // private
27916     onMouseUp : function(e){
27917         if(e.button == 0){
27918             this.el.removeClass("x-btn-click");
27919             Roo.get(document).un('mouseup', this.onMouseUp, this);
27920         }
27921     },
27922     // private
27923     onMenuShow : function(e){
27924         this.el.addClass("x-btn-menu-active");
27925     },
27926     // private
27927     onMenuHide : function(e){
27928         this.el.removeClass("x-btn-menu-active");
27929     }   
27930 });
27931
27932 // Private utility class used by Button
27933 Roo.ButtonToggleMgr = function(){
27934    var groups = {};
27935    
27936    function toggleGroup(btn, state){
27937        if(state){
27938            var g = groups[btn.toggleGroup];
27939            for(var i = 0, l = g.length; i < l; i++){
27940                if(g[i] != btn){
27941                    g[i].toggle(false);
27942                }
27943            }
27944        }
27945    }
27946    
27947    return {
27948        register : function(btn){
27949            if(!btn.toggleGroup){
27950                return;
27951            }
27952            var g = groups[btn.toggleGroup];
27953            if(!g){
27954                g = groups[btn.toggleGroup] = [];
27955            }
27956            g.push(btn);
27957            btn.on("toggle", toggleGroup);
27958        },
27959        
27960        unregister : function(btn){
27961            if(!btn.toggleGroup){
27962                return;
27963            }
27964            var g = groups[btn.toggleGroup];
27965            if(g){
27966                g.remove(btn);
27967                btn.un("toggle", toggleGroup);
27968            }
27969        }
27970    };
27971 }();/*
27972  * Based on:
27973  * Ext JS Library 1.1.1
27974  * Copyright(c) 2006-2007, Ext JS, LLC.
27975  *
27976  * Originally Released Under LGPL - original licence link has changed is not relivant.
27977  *
27978  * Fork - LGPL
27979  * <script type="text/javascript">
27980  */
27981  
27982 /**
27983  * @class Roo.SplitButton
27984  * @extends Roo.Button
27985  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27986  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27987  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27988  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27989  * @cfg {String} arrowTooltip The title attribute of the arrow
27990  * @constructor
27991  * Create a new menu button
27992  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27993  * @param {Object} config The config object
27994  */
27995 Roo.SplitButton = function(renderTo, config){
27996     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27997     /**
27998      * @event arrowclick
27999      * Fires when this button's arrow is clicked
28000      * @param {SplitButton} this
28001      * @param {EventObject} e The click event
28002      */
28003     this.addEvents({"arrowclick":true});
28004 };
28005
28006 Roo.extend(Roo.SplitButton, Roo.Button, {
28007     render : function(renderTo){
28008         // this is one sweet looking template!
28009         var tpl = new Roo.Template(
28010             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28011             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28012             '<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>',
28013             "</tbody></table></td><td>",
28014             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28015             '<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>',
28016             "</tbody></table></td></tr></table>"
28017         );
28018         var btn = tpl.append(renderTo, [this.text, this.type], true);
28019         var btnEl = btn.child("button");
28020         if(this.cls){
28021             btn.addClass(this.cls);
28022         }
28023         if(this.icon){
28024             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28025         }
28026         if(this.iconCls){
28027             btnEl.addClass(this.iconCls);
28028             if(!this.cls){
28029                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28030             }
28031         }
28032         this.el = btn;
28033         if(this.handleMouseEvents){
28034             btn.on("mouseover", this.onMouseOver, this);
28035             btn.on("mouseout", this.onMouseOut, this);
28036             btn.on("mousedown", this.onMouseDown, this);
28037             btn.on("mouseup", this.onMouseUp, this);
28038         }
28039         btn.on(this.clickEvent, this.onClick, this);
28040         if(this.tooltip){
28041             if(typeof this.tooltip == 'object'){
28042                 Roo.QuickTips.tips(Roo.apply({
28043                       target: btnEl.id
28044                 }, this.tooltip));
28045             } else {
28046                 btnEl.dom[this.tooltipType] = this.tooltip;
28047             }
28048         }
28049         if(this.arrowTooltip){
28050             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28051         }
28052         if(this.hidden){
28053             this.hide();
28054         }
28055         if(this.disabled){
28056             this.disable();
28057         }
28058         if(this.pressed){
28059             this.el.addClass("x-btn-pressed");
28060         }
28061         if(Roo.isIE && !Roo.isIE7){
28062             this.autoWidth.defer(1, this);
28063         }else{
28064             this.autoWidth();
28065         }
28066         if(this.menu){
28067             this.menu.on("show", this.onMenuShow, this);
28068             this.menu.on("hide", this.onMenuHide, this);
28069         }
28070         this.fireEvent('render', this);
28071     },
28072
28073     // private
28074     autoWidth : function(){
28075         if(this.el){
28076             var tbl = this.el.child("table:first");
28077             var tbl2 = this.el.child("table:last");
28078             this.el.setWidth("auto");
28079             tbl.setWidth("auto");
28080             if(Roo.isIE7 && Roo.isStrict){
28081                 var ib = this.el.child('button:first');
28082                 if(ib && ib.getWidth() > 20){
28083                     ib.clip();
28084                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28085                 }
28086             }
28087             if(this.minWidth){
28088                 if(this.hidden){
28089                     this.el.beginMeasure();
28090                 }
28091                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28092                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28093                 }
28094                 if(this.hidden){
28095                     this.el.endMeasure();
28096                 }
28097             }
28098             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28099         } 
28100     },
28101     /**
28102      * Sets this button's click handler
28103      * @param {Function} handler The function to call when the button is clicked
28104      * @param {Object} scope (optional) Scope for the function passed above
28105      */
28106     setHandler : function(handler, scope){
28107         this.handler = handler;
28108         this.scope = scope;  
28109     },
28110     
28111     /**
28112      * Sets this button's arrow click handler
28113      * @param {Function} handler The function to call when the arrow is clicked
28114      * @param {Object} scope (optional) Scope for the function passed above
28115      */
28116     setArrowHandler : function(handler, scope){
28117         this.arrowHandler = handler;
28118         this.scope = scope;  
28119     },
28120     
28121     /**
28122      * Focus the button
28123      */
28124     focus : function(){
28125         if(this.el){
28126             this.el.child("button:first").focus();
28127         }
28128     },
28129
28130     // private
28131     onClick : function(e){
28132         e.preventDefault();
28133         if(!this.disabled){
28134             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28135                 if(this.menu && !this.menu.isVisible()){
28136                     this.menu.show(this.el, this.menuAlign);
28137                 }
28138                 this.fireEvent("arrowclick", this, e);
28139                 if(this.arrowHandler){
28140                     this.arrowHandler.call(this.scope || this, this, e);
28141                 }
28142             }else{
28143                 this.fireEvent("click", this, e);
28144                 if(this.handler){
28145                     this.handler.call(this.scope || this, this, e);
28146                 }
28147             }
28148         }
28149     },
28150     // private
28151     onMouseDown : function(e){
28152         if(!this.disabled){
28153             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28154         }
28155     },
28156     // private
28157     onMouseUp : function(e){
28158         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28159     }   
28160 });
28161
28162
28163 // backwards compat
28164 Roo.MenuButton = Roo.SplitButton;/*
28165  * Based on:
28166  * Ext JS Library 1.1.1
28167  * Copyright(c) 2006-2007, Ext JS, LLC.
28168  *
28169  * Originally Released Under LGPL - original licence link has changed is not relivant.
28170  *
28171  * Fork - LGPL
28172  * <script type="text/javascript">
28173  */
28174
28175 /**
28176  * @class Roo.Toolbar
28177  * Basic Toolbar class.
28178  * @constructor
28179  * Creates a new Toolbar
28180  * @param {Object} container The config object
28181  */ 
28182 Roo.Toolbar = function(container, buttons, config)
28183 {
28184     /// old consturctor format still supported..
28185     if(container instanceof Array){ // omit the container for later rendering
28186         buttons = container;
28187         config = buttons;
28188         container = null;
28189     }
28190     if (typeof(container) == 'object' && container.xtype) {
28191         config = container;
28192         container = config.container;
28193         buttons = config.buttons || []; // not really - use items!!
28194     }
28195     var xitems = [];
28196     if (config && config.items) {
28197         xitems = config.items;
28198         delete config.items;
28199     }
28200     Roo.apply(this, config);
28201     this.buttons = buttons;
28202     
28203     if(container){
28204         this.render(container);
28205     }
28206     this.xitems = xitems;
28207     Roo.each(xitems, function(b) {
28208         this.add(b);
28209     }, this);
28210     
28211 };
28212
28213 Roo.Toolbar.prototype = {
28214     /**
28215      * @cfg {Array} items
28216      * array of button configs or elements to add (will be converted to a MixedCollection)
28217      */
28218     
28219     /**
28220      * @cfg {String/HTMLElement/Element} container
28221      * The id or element that will contain the toolbar
28222      */
28223     // private
28224     render : function(ct){
28225         this.el = Roo.get(ct);
28226         if(this.cls){
28227             this.el.addClass(this.cls);
28228         }
28229         // using a table allows for vertical alignment
28230         // 100% width is needed by Safari...
28231         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28232         this.tr = this.el.child("tr", true);
28233         var autoId = 0;
28234         this.items = new Roo.util.MixedCollection(false, function(o){
28235             return o.id || ("item" + (++autoId));
28236         });
28237         if(this.buttons){
28238             this.add.apply(this, this.buttons);
28239             delete this.buttons;
28240         }
28241     },
28242
28243     /**
28244      * Adds element(s) to the toolbar -- this function takes a variable number of 
28245      * arguments of mixed type and adds them to the toolbar.
28246      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28247      * <ul>
28248      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28249      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28250      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28251      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28252      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28253      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28254      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28255      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28256      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28257      * </ul>
28258      * @param {Mixed} arg2
28259      * @param {Mixed} etc.
28260      */
28261     add : function(){
28262         var a = arguments, l = a.length;
28263         for(var i = 0; i < l; i++){
28264             this._add(a[i]);
28265         }
28266     },
28267     // private..
28268     _add : function(el) {
28269         
28270         if (el.xtype) {
28271             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28272         }
28273         
28274         if (el.applyTo){ // some kind of form field
28275             return this.addField(el);
28276         } 
28277         if (el.render){ // some kind of Toolbar.Item
28278             return this.addItem(el);
28279         }
28280         if (typeof el == "string"){ // string
28281             if(el == "separator" || el == "-"){
28282                 return this.addSeparator();
28283             }
28284             if (el == " "){
28285                 return this.addSpacer();
28286             }
28287             if(el == "->"){
28288                 return this.addFill();
28289             }
28290             return this.addText(el);
28291             
28292         }
28293         if(el.tagName){ // element
28294             return this.addElement(el);
28295         }
28296         if(typeof el == "object"){ // must be button config?
28297             return this.addButton(el);
28298         }
28299         // and now what?!?!
28300         return false;
28301         
28302     },
28303     
28304     /**
28305      * Add an Xtype element
28306      * @param {Object} xtype Xtype Object
28307      * @return {Object} created Object
28308      */
28309     addxtype : function(e){
28310         return this.add(e);  
28311     },
28312     
28313     /**
28314      * Returns the Element for this toolbar.
28315      * @return {Roo.Element}
28316      */
28317     getEl : function(){
28318         return this.el;  
28319     },
28320     
28321     /**
28322      * Adds a separator
28323      * @return {Roo.Toolbar.Item} The separator item
28324      */
28325     addSeparator : function(){
28326         return this.addItem(new Roo.Toolbar.Separator());
28327     },
28328
28329     /**
28330      * Adds a spacer element
28331      * @return {Roo.Toolbar.Spacer} The spacer item
28332      */
28333     addSpacer : function(){
28334         return this.addItem(new Roo.Toolbar.Spacer());
28335     },
28336
28337     /**
28338      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28339      * @return {Roo.Toolbar.Fill} The fill item
28340      */
28341     addFill : function(){
28342         return this.addItem(new Roo.Toolbar.Fill());
28343     },
28344
28345     /**
28346      * Adds any standard HTML element to the toolbar
28347      * @param {String/HTMLElement/Element} el The element or id of the element to add
28348      * @return {Roo.Toolbar.Item} The element's item
28349      */
28350     addElement : function(el){
28351         return this.addItem(new Roo.Toolbar.Item(el));
28352     },
28353     /**
28354      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28355      * @type Roo.util.MixedCollection  
28356      */
28357     items : false,
28358      
28359     /**
28360      * Adds any Toolbar.Item or subclass
28361      * @param {Roo.Toolbar.Item} item
28362      * @return {Roo.Toolbar.Item} The item
28363      */
28364     addItem : function(item){
28365         var td = this.nextBlock();
28366         item.render(td);
28367         this.items.add(item);
28368         return item;
28369     },
28370     
28371     /**
28372      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28373      * @param {Object/Array} config A button config or array of configs
28374      * @return {Roo.Toolbar.Button/Array}
28375      */
28376     addButton : function(config){
28377         if(config instanceof Array){
28378             var buttons = [];
28379             for(var i = 0, len = config.length; i < len; i++) {
28380                 buttons.push(this.addButton(config[i]));
28381             }
28382             return buttons;
28383         }
28384         var b = config;
28385         if(!(config instanceof Roo.Toolbar.Button)){
28386             b = config.split ?
28387                 new Roo.Toolbar.SplitButton(config) :
28388                 new Roo.Toolbar.Button(config);
28389         }
28390         var td = this.nextBlock();
28391         b.render(td);
28392         this.items.add(b);
28393         return b;
28394     },
28395     
28396     /**
28397      * Adds text to the toolbar
28398      * @param {String} text The text to add
28399      * @return {Roo.Toolbar.Item} The element's item
28400      */
28401     addText : function(text){
28402         return this.addItem(new Roo.Toolbar.TextItem(text));
28403     },
28404     
28405     /**
28406      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28407      * @param {Number} index The index where the item is to be inserted
28408      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28409      * @return {Roo.Toolbar.Button/Item}
28410      */
28411     insertButton : function(index, item){
28412         if(item instanceof Array){
28413             var buttons = [];
28414             for(var i = 0, len = item.length; i < len; i++) {
28415                buttons.push(this.insertButton(index + i, item[i]));
28416             }
28417             return buttons;
28418         }
28419         if (!(item instanceof Roo.Toolbar.Button)){
28420            item = new Roo.Toolbar.Button(item);
28421         }
28422         var td = document.createElement("td");
28423         this.tr.insertBefore(td, this.tr.childNodes[index]);
28424         item.render(td);
28425         this.items.insert(index, item);
28426         return item;
28427     },
28428     
28429     /**
28430      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28431      * @param {Object} config
28432      * @return {Roo.Toolbar.Item} The element's item
28433      */
28434     addDom : function(config, returnEl){
28435         var td = this.nextBlock();
28436         Roo.DomHelper.overwrite(td, config);
28437         var ti = new Roo.Toolbar.Item(td.firstChild);
28438         ti.render(td);
28439         this.items.add(ti);
28440         return ti;
28441     },
28442
28443     /**
28444      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28445      * @type Roo.util.MixedCollection  
28446      */
28447     fields : false,
28448     
28449     /**
28450      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28451      * Note: the field should not have been rendered yet. For a field that has already been
28452      * rendered, use {@link #addElement}.
28453      * @param {Roo.form.Field} field
28454      * @return {Roo.ToolbarItem}
28455      */
28456      
28457       
28458     addField : function(field) {
28459         if (!this.fields) {
28460             var autoId = 0;
28461             this.fields = new Roo.util.MixedCollection(false, function(o){
28462                 return o.id || ("item" + (++autoId));
28463             });
28464
28465         }
28466         
28467         var td = this.nextBlock();
28468         field.render(td);
28469         var ti = new Roo.Toolbar.Item(td.firstChild);
28470         ti.render(td);
28471         this.items.add(ti);
28472         this.fields.add(field);
28473         return ti;
28474     },
28475     /**
28476      * Hide the toolbar
28477      * @method hide
28478      */
28479      
28480       
28481     hide : function()
28482     {
28483         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28484         this.el.child('div').hide();
28485     },
28486     /**
28487      * Show the toolbar
28488      * @method show
28489      */
28490     show : function()
28491     {
28492         this.el.child('div').show();
28493     },
28494       
28495     // private
28496     nextBlock : function(){
28497         var td = document.createElement("td");
28498         this.tr.appendChild(td);
28499         return td;
28500     },
28501
28502     // private
28503     destroy : function(){
28504         if(this.items){ // rendered?
28505             Roo.destroy.apply(Roo, this.items.items);
28506         }
28507         if(this.fields){ // rendered?
28508             Roo.destroy.apply(Roo, this.fields.items);
28509         }
28510         Roo.Element.uncache(this.el, this.tr);
28511     }
28512 };
28513
28514 /**
28515  * @class Roo.Toolbar.Item
28516  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28517  * @constructor
28518  * Creates a new Item
28519  * @param {HTMLElement} el 
28520  */
28521 Roo.Toolbar.Item = function(el){
28522     var cfg = {};
28523     if (typeof (el.xtype) != 'undefined') {
28524         cfg = el;
28525         el = cfg.el;
28526     }
28527     
28528     this.el = Roo.getDom(el);
28529     this.id = Roo.id(this.el);
28530     this.hidden = false;
28531     
28532     this.addEvents({
28533          /**
28534              * @event render
28535              * Fires when the button is rendered
28536              * @param {Button} this
28537              */
28538         'render': true
28539     });
28540     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28541 };
28542 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28543 //Roo.Toolbar.Item.prototype = {
28544     
28545     /**
28546      * Get this item's HTML Element
28547      * @return {HTMLElement}
28548      */
28549     getEl : function(){
28550        return this.el;  
28551     },
28552
28553     // private
28554     render : function(td){
28555         
28556          this.td = td;
28557         td.appendChild(this.el);
28558         
28559         this.fireEvent('render', this);
28560     },
28561     
28562     /**
28563      * Removes and destroys this item.
28564      */
28565     destroy : function(){
28566         this.td.parentNode.removeChild(this.td);
28567     },
28568     
28569     /**
28570      * Shows this item.
28571      */
28572     show: function(){
28573         this.hidden = false;
28574         this.td.style.display = "";
28575     },
28576     
28577     /**
28578      * Hides this item.
28579      */
28580     hide: function(){
28581         this.hidden = true;
28582         this.td.style.display = "none";
28583     },
28584     
28585     /**
28586      * Convenience function for boolean show/hide.
28587      * @param {Boolean} visible true to show/false to hide
28588      */
28589     setVisible: function(visible){
28590         if(visible) {
28591             this.show();
28592         }else{
28593             this.hide();
28594         }
28595     },
28596     
28597     /**
28598      * Try to focus this item.
28599      */
28600     focus : function(){
28601         Roo.fly(this.el).focus();
28602     },
28603     
28604     /**
28605      * Disables this item.
28606      */
28607     disable : function(){
28608         Roo.fly(this.td).addClass("x-item-disabled");
28609         this.disabled = true;
28610         this.el.disabled = true;
28611     },
28612     
28613     /**
28614      * Enables this item.
28615      */
28616     enable : function(){
28617         Roo.fly(this.td).removeClass("x-item-disabled");
28618         this.disabled = false;
28619         this.el.disabled = false;
28620     }
28621 });
28622
28623
28624 /**
28625  * @class Roo.Toolbar.Separator
28626  * @extends Roo.Toolbar.Item
28627  * A simple toolbar separator class
28628  * @constructor
28629  * Creates a new Separator
28630  */
28631 Roo.Toolbar.Separator = function(cfg){
28632     
28633     var s = document.createElement("span");
28634     s.className = "ytb-sep";
28635     if (cfg) {
28636         cfg.el = s;
28637     }
28638     
28639     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28640 };
28641 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28642     enable:Roo.emptyFn,
28643     disable:Roo.emptyFn,
28644     focus:Roo.emptyFn
28645 });
28646
28647 /**
28648  * @class Roo.Toolbar.Spacer
28649  * @extends Roo.Toolbar.Item
28650  * A simple element that adds extra horizontal space to a toolbar.
28651  * @constructor
28652  * Creates a new Spacer
28653  */
28654 Roo.Toolbar.Spacer = function(cfg){
28655     var s = document.createElement("div");
28656     s.className = "ytb-spacer";
28657     if (cfg) {
28658         cfg.el = s;
28659     }
28660     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28661 };
28662 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28663     enable:Roo.emptyFn,
28664     disable:Roo.emptyFn,
28665     focus:Roo.emptyFn
28666 });
28667
28668 /**
28669  * @class Roo.Toolbar.Fill
28670  * @extends Roo.Toolbar.Spacer
28671  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28672  * @constructor
28673  * Creates a new Spacer
28674  */
28675 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28676     // private
28677     render : function(td){
28678         td.style.width = '100%';
28679         Roo.Toolbar.Fill.superclass.render.call(this, td);
28680     }
28681 });
28682
28683 /**
28684  * @class Roo.Toolbar.TextItem
28685  * @extends Roo.Toolbar.Item
28686  * A simple class that renders text directly into a toolbar.
28687  * @constructor
28688  * Creates a new TextItem
28689  * @param {String} text
28690  */
28691 Roo.Toolbar.TextItem = function(cfg){
28692     var  text = cfg || "";
28693     if (typeof(cfg) == 'object') {
28694         text = cfg.text || "";
28695     }  else {
28696         cfg = null;
28697     }
28698     var s = document.createElement("span");
28699     s.className = "ytb-text";
28700     s.innerHTML = text;
28701     if (cfg) {
28702         cfg.el  = s;
28703     }
28704     
28705     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28706 };
28707 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28708     
28709      
28710     enable:Roo.emptyFn,
28711     disable:Roo.emptyFn,
28712     focus:Roo.emptyFn
28713 });
28714
28715 /**
28716  * @class Roo.Toolbar.Button
28717  * @extends Roo.Button
28718  * A button that renders into a toolbar.
28719  * @constructor
28720  * Creates a new Button
28721  * @param {Object} config A standard {@link Roo.Button} config object
28722  */
28723 Roo.Toolbar.Button = function(config){
28724     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28725 };
28726 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28727     render : function(td){
28728         this.td = td;
28729         Roo.Toolbar.Button.superclass.render.call(this, td);
28730     },
28731     
28732     /**
28733      * Removes and destroys this button
28734      */
28735     destroy : function(){
28736         Roo.Toolbar.Button.superclass.destroy.call(this);
28737         this.td.parentNode.removeChild(this.td);
28738     },
28739     
28740     /**
28741      * Shows this button
28742      */
28743     show: function(){
28744         this.hidden = false;
28745         this.td.style.display = "";
28746     },
28747     
28748     /**
28749      * Hides this button
28750      */
28751     hide: function(){
28752         this.hidden = true;
28753         this.td.style.display = "none";
28754     },
28755
28756     /**
28757      * Disables this item
28758      */
28759     disable : function(){
28760         Roo.fly(this.td).addClass("x-item-disabled");
28761         this.disabled = true;
28762     },
28763
28764     /**
28765      * Enables this item
28766      */
28767     enable : function(){
28768         Roo.fly(this.td).removeClass("x-item-disabled");
28769         this.disabled = false;
28770     }
28771 });
28772 // backwards compat
28773 Roo.ToolbarButton = Roo.Toolbar.Button;
28774
28775 /**
28776  * @class Roo.Toolbar.SplitButton
28777  * @extends Roo.SplitButton
28778  * A menu button that renders into a toolbar.
28779  * @constructor
28780  * Creates a new SplitButton
28781  * @param {Object} config A standard {@link Roo.SplitButton} config object
28782  */
28783 Roo.Toolbar.SplitButton = function(config){
28784     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28785 };
28786 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28787     render : function(td){
28788         this.td = td;
28789         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28790     },
28791     
28792     /**
28793      * Removes and destroys this button
28794      */
28795     destroy : function(){
28796         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28797         this.td.parentNode.removeChild(this.td);
28798     },
28799     
28800     /**
28801      * Shows this button
28802      */
28803     show: function(){
28804         this.hidden = false;
28805         this.td.style.display = "";
28806     },
28807     
28808     /**
28809      * Hides this button
28810      */
28811     hide: function(){
28812         this.hidden = true;
28813         this.td.style.display = "none";
28814     }
28815 });
28816
28817 // backwards compat
28818 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28819  * Based on:
28820  * Ext JS Library 1.1.1
28821  * Copyright(c) 2006-2007, Ext JS, LLC.
28822  *
28823  * Originally Released Under LGPL - original licence link has changed is not relivant.
28824  *
28825  * Fork - LGPL
28826  * <script type="text/javascript">
28827  */
28828  
28829 /**
28830  * @class Roo.PagingToolbar
28831  * @extends Roo.Toolbar
28832  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28833  * @constructor
28834  * Create a new PagingToolbar
28835  * @param {Object} config The config object
28836  */
28837 Roo.PagingToolbar = function(el, ds, config)
28838 {
28839     // old args format still supported... - xtype is prefered..
28840     if (typeof(el) == 'object' && el.xtype) {
28841         // created from xtype...
28842         config = el;
28843         ds = el.dataSource;
28844         el = config.container;
28845     }
28846     var items = [];
28847     if (config.items) {
28848         items = config.items;
28849         config.items = [];
28850     }
28851     
28852     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28853     this.ds = ds;
28854     this.cursor = 0;
28855     this.renderButtons(this.el);
28856     this.bind(ds);
28857     
28858     // supprot items array.
28859    
28860     Roo.each(items, function(e) {
28861         this.add(Roo.factory(e));
28862     },this);
28863     
28864 };
28865
28866 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28867     /**
28868      * @cfg {Roo.data.Store} dataSource
28869      * The underlying data store providing the paged data
28870      */
28871     /**
28872      * @cfg {String/HTMLElement/Element} container
28873      * container The id or element that will contain the toolbar
28874      */
28875     /**
28876      * @cfg {Boolean} displayInfo
28877      * True to display the displayMsg (defaults to false)
28878      */
28879     /**
28880      * @cfg {Number} pageSize
28881      * The number of records to display per page (defaults to 20)
28882      */
28883     pageSize: 20,
28884     /**
28885      * @cfg {String} displayMsg
28886      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28887      */
28888     displayMsg : 'Displaying {0} - {1} of {2}',
28889     /**
28890      * @cfg {String} emptyMsg
28891      * The message to display when no records are found (defaults to "No data to display")
28892      */
28893     emptyMsg : 'No data to display',
28894     /**
28895      * Customizable piece of the default paging text (defaults to "Page")
28896      * @type String
28897      */
28898     beforePageText : "Page",
28899     /**
28900      * Customizable piece of the default paging text (defaults to "of %0")
28901      * @type String
28902      */
28903     afterPageText : "of {0}",
28904     /**
28905      * Customizable piece of the default paging text (defaults to "First Page")
28906      * @type String
28907      */
28908     firstText : "First Page",
28909     /**
28910      * Customizable piece of the default paging text (defaults to "Previous Page")
28911      * @type String
28912      */
28913     prevText : "Previous Page",
28914     /**
28915      * Customizable piece of the default paging text (defaults to "Next Page")
28916      * @type String
28917      */
28918     nextText : "Next Page",
28919     /**
28920      * Customizable piece of the default paging text (defaults to "Last Page")
28921      * @type String
28922      */
28923     lastText : "Last Page",
28924     /**
28925      * Customizable piece of the default paging text (defaults to "Refresh")
28926      * @type String
28927      */
28928     refreshText : "Refresh",
28929
28930     // private
28931     renderButtons : function(el){
28932         Roo.PagingToolbar.superclass.render.call(this, el);
28933         this.first = this.addButton({
28934             tooltip: this.firstText,
28935             cls: "x-btn-icon x-grid-page-first",
28936             disabled: true,
28937             handler: this.onClick.createDelegate(this, ["first"])
28938         });
28939         this.prev = this.addButton({
28940             tooltip: this.prevText,
28941             cls: "x-btn-icon x-grid-page-prev",
28942             disabled: true,
28943             handler: this.onClick.createDelegate(this, ["prev"])
28944         });
28945         //this.addSeparator();
28946         this.add(this.beforePageText);
28947         this.field = Roo.get(this.addDom({
28948            tag: "input",
28949            type: "text",
28950            size: "3",
28951            value: "1",
28952            cls: "x-grid-page-number"
28953         }).el);
28954         this.field.on("keydown", this.onPagingKeydown, this);
28955         this.field.on("focus", function(){this.dom.select();});
28956         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28957         this.field.setHeight(18);
28958         //this.addSeparator();
28959         this.next = this.addButton({
28960             tooltip: this.nextText,
28961             cls: "x-btn-icon x-grid-page-next",
28962             disabled: true,
28963             handler: this.onClick.createDelegate(this, ["next"])
28964         });
28965         this.last = this.addButton({
28966             tooltip: this.lastText,
28967             cls: "x-btn-icon x-grid-page-last",
28968             disabled: true,
28969             handler: this.onClick.createDelegate(this, ["last"])
28970         });
28971         //this.addSeparator();
28972         this.loading = this.addButton({
28973             tooltip: this.refreshText,
28974             cls: "x-btn-icon x-grid-loading",
28975             handler: this.onClick.createDelegate(this, ["refresh"])
28976         });
28977
28978         if(this.displayInfo){
28979             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28980         }
28981     },
28982
28983     // private
28984     updateInfo : function(){
28985         if(this.displayEl){
28986             var count = this.ds.getCount();
28987             var msg = count == 0 ?
28988                 this.emptyMsg :
28989                 String.format(
28990                     this.displayMsg,
28991                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28992                 );
28993             this.displayEl.update(msg);
28994         }
28995     },
28996
28997     // private
28998     onLoad : function(ds, r, o){
28999        this.cursor = o.params ? o.params.start : 0;
29000        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29001
29002        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29003        this.field.dom.value = ap;
29004        this.first.setDisabled(ap == 1);
29005        this.prev.setDisabled(ap == 1);
29006        this.next.setDisabled(ap == ps);
29007        this.last.setDisabled(ap == ps);
29008        this.loading.enable();
29009        this.updateInfo();
29010     },
29011
29012     // private
29013     getPageData : function(){
29014         var total = this.ds.getTotalCount();
29015         return {
29016             total : total,
29017             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29018             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29019         };
29020     },
29021
29022     // private
29023     onLoadError : function(){
29024         this.loading.enable();
29025     },
29026
29027     // private
29028     onPagingKeydown : function(e){
29029         var k = e.getKey();
29030         var d = this.getPageData();
29031         if(k == e.RETURN){
29032             var v = this.field.dom.value, pageNum;
29033             if(!v || isNaN(pageNum = parseInt(v, 10))){
29034                 this.field.dom.value = d.activePage;
29035                 return;
29036             }
29037             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29038             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29039             e.stopEvent();
29040         }
29041         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))
29042         {
29043           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29044           this.field.dom.value = pageNum;
29045           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29046           e.stopEvent();
29047         }
29048         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29049         {
29050           var v = this.field.dom.value, pageNum; 
29051           var increment = (e.shiftKey) ? 10 : 1;
29052           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29053             increment *= -1;
29054           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29055             this.field.dom.value = d.activePage;
29056             return;
29057           }
29058           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29059           {
29060             this.field.dom.value = parseInt(v, 10) + increment;
29061             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29062             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29063           }
29064           e.stopEvent();
29065         }
29066     },
29067
29068     // private
29069     beforeLoad : function(){
29070         if(this.loading){
29071             this.loading.disable();
29072         }
29073     },
29074
29075     // private
29076     onClick : function(which){
29077         var ds = this.ds;
29078         switch(which){
29079             case "first":
29080                 ds.load({params:{start: 0, limit: this.pageSize}});
29081             break;
29082             case "prev":
29083                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29084             break;
29085             case "next":
29086                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29087             break;
29088             case "last":
29089                 var total = ds.getTotalCount();
29090                 var extra = total % this.pageSize;
29091                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29092                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29093             break;
29094             case "refresh":
29095                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29096             break;
29097         }
29098     },
29099
29100     /**
29101      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29102      * @param {Roo.data.Store} store The data store to unbind
29103      */
29104     unbind : function(ds){
29105         ds.un("beforeload", this.beforeLoad, this);
29106         ds.un("load", this.onLoad, this);
29107         ds.un("loadexception", this.onLoadError, this);
29108         ds.un("remove", this.updateInfo, this);
29109         ds.un("add", this.updateInfo, this);
29110         this.ds = undefined;
29111     },
29112
29113     /**
29114      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29115      * @param {Roo.data.Store} store The data store to bind
29116      */
29117     bind : function(ds){
29118         ds.on("beforeload", this.beforeLoad, this);
29119         ds.on("load", this.onLoad, this);
29120         ds.on("loadexception", this.onLoadError, this);
29121         ds.on("remove", this.updateInfo, this);
29122         ds.on("add", this.updateInfo, this);
29123         this.ds = ds;
29124     }
29125 });/*
29126  * Based on:
29127  * Ext JS Library 1.1.1
29128  * Copyright(c) 2006-2007, Ext JS, LLC.
29129  *
29130  * Originally Released Under LGPL - original licence link has changed is not relivant.
29131  *
29132  * Fork - LGPL
29133  * <script type="text/javascript">
29134  */
29135
29136 /**
29137  * @class Roo.Resizable
29138  * @extends Roo.util.Observable
29139  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29140  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29141  * 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
29142  * the element will be wrapped for you automatically.</p>
29143  * <p>Here is the list of valid resize handles:</p>
29144  * <pre>
29145 Value   Description
29146 ------  -------------------
29147  'n'     north
29148  's'     south
29149  'e'     east
29150  'w'     west
29151  'nw'    northwest
29152  'sw'    southwest
29153  'se'    southeast
29154  'ne'    northeast
29155  'hd'    horizontal drag
29156  'all'   all
29157 </pre>
29158  * <p>Here's an example showing the creation of a typical Resizable:</p>
29159  * <pre><code>
29160 var resizer = new Roo.Resizable("element-id", {
29161     handles: 'all',
29162     minWidth: 200,
29163     minHeight: 100,
29164     maxWidth: 500,
29165     maxHeight: 400,
29166     pinned: true
29167 });
29168 resizer.on("resize", myHandler);
29169 </code></pre>
29170  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29171  * resizer.east.setDisplayed(false);</p>
29172  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29173  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29174  * resize operation's new size (defaults to [0, 0])
29175  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29176  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29177  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29178  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29179  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29180  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29181  * @cfg {Number} width The width of the element in pixels (defaults to null)
29182  * @cfg {Number} height The height of the element in pixels (defaults to null)
29183  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29184  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29185  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29186  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29187  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29188  * in favor of the handles config option (defaults to false)
29189  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29190  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29191  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29192  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29193  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29194  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29195  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29196  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29197  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29198  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29199  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29200  * @constructor
29201  * Create a new resizable component
29202  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29203  * @param {Object} config configuration options
29204   */
29205 Roo.Resizable = function(el, config)
29206 {
29207     this.el = Roo.get(el);
29208
29209     if(config && config.wrap){
29210         config.resizeChild = this.el;
29211         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29212         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29213         this.el.setStyle("overflow", "hidden");
29214         this.el.setPositioning(config.resizeChild.getPositioning());
29215         config.resizeChild.clearPositioning();
29216         if(!config.width || !config.height){
29217             var csize = config.resizeChild.getSize();
29218             this.el.setSize(csize.width, csize.height);
29219         }
29220         if(config.pinned && !config.adjustments){
29221             config.adjustments = "auto";
29222         }
29223     }
29224
29225     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29226     this.proxy.unselectable();
29227     this.proxy.enableDisplayMode('block');
29228
29229     Roo.apply(this, config);
29230
29231     if(this.pinned){
29232         this.disableTrackOver = true;
29233         this.el.addClass("x-resizable-pinned");
29234     }
29235     // if the element isn't positioned, make it relative
29236     var position = this.el.getStyle("position");
29237     if(position != "absolute" && position != "fixed"){
29238         this.el.setStyle("position", "relative");
29239     }
29240     if(!this.handles){ // no handles passed, must be legacy style
29241         this.handles = 's,e,se';
29242         if(this.multiDirectional){
29243             this.handles += ',n,w';
29244         }
29245     }
29246     if(this.handles == "all"){
29247         this.handles = "n s e w ne nw se sw";
29248     }
29249     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29250     var ps = Roo.Resizable.positions;
29251     for(var i = 0, len = hs.length; i < len; i++){
29252         if(hs[i] && ps[hs[i]]){
29253             var pos = ps[hs[i]];
29254             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29255         }
29256     }
29257     // legacy
29258     this.corner = this.southeast;
29259     
29260     // updateBox = the box can move..
29261     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29262         this.updateBox = true;
29263     }
29264
29265     this.activeHandle = null;
29266
29267     if(this.resizeChild){
29268         if(typeof this.resizeChild == "boolean"){
29269             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29270         }else{
29271             this.resizeChild = Roo.get(this.resizeChild, true);
29272         }
29273     }
29274     
29275     if(this.adjustments == "auto"){
29276         var rc = this.resizeChild;
29277         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29278         if(rc && (hw || hn)){
29279             rc.position("relative");
29280             rc.setLeft(hw ? hw.el.getWidth() : 0);
29281             rc.setTop(hn ? hn.el.getHeight() : 0);
29282         }
29283         this.adjustments = [
29284             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29285             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29286         ];
29287     }
29288
29289     if(this.draggable){
29290         this.dd = this.dynamic ?
29291             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29292         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29293     }
29294
29295     // public events
29296     this.addEvents({
29297         /**
29298          * @event beforeresize
29299          * Fired before resize is allowed. Set enabled to false to cancel resize.
29300          * @param {Roo.Resizable} this
29301          * @param {Roo.EventObject} e The mousedown event
29302          */
29303         "beforeresize" : true,
29304         /**
29305          * @event resizing
29306          * Fired a resizing.
29307          * @param {Roo.Resizable} this
29308          * @param {Number} x The new x position
29309          * @param {Number} y The new y position
29310          * @param {Number} w The new w width
29311          * @param {Number} h The new h hight
29312          * @param {Roo.EventObject} e The mouseup event
29313          */
29314         "resizing" : true,
29315         /**
29316          * @event resize
29317          * Fired after a resize.
29318          * @param {Roo.Resizable} this
29319          * @param {Number} width The new width
29320          * @param {Number} height The new height
29321          * @param {Roo.EventObject} e The mouseup event
29322          */
29323         "resize" : true
29324     });
29325
29326     if(this.width !== null && this.height !== null){
29327         this.resizeTo(this.width, this.height);
29328     }else{
29329         this.updateChildSize();
29330     }
29331     if(Roo.isIE){
29332         this.el.dom.style.zoom = 1;
29333     }
29334     Roo.Resizable.superclass.constructor.call(this);
29335 };
29336
29337 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29338         resizeChild : false,
29339         adjustments : [0, 0],
29340         minWidth : 5,
29341         minHeight : 5,
29342         maxWidth : 10000,
29343         maxHeight : 10000,
29344         enabled : true,
29345         animate : false,
29346         duration : .35,
29347         dynamic : false,
29348         handles : false,
29349         multiDirectional : false,
29350         disableTrackOver : false,
29351         easing : 'easeOutStrong',
29352         widthIncrement : 0,
29353         heightIncrement : 0,
29354         pinned : false,
29355         width : null,
29356         height : null,
29357         preserveRatio : false,
29358         transparent: false,
29359         minX: 0,
29360         minY: 0,
29361         draggable: false,
29362
29363         /**
29364          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29365          */
29366         constrainTo: undefined,
29367         /**
29368          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29369          */
29370         resizeRegion: undefined,
29371
29372
29373     /**
29374      * Perform a manual resize
29375      * @param {Number} width
29376      * @param {Number} height
29377      */
29378     resizeTo : function(width, height){
29379         this.el.setSize(width, height);
29380         this.updateChildSize();
29381         this.fireEvent("resize", this, width, height, null);
29382     },
29383
29384     // private
29385     startSizing : function(e, handle){
29386         this.fireEvent("beforeresize", this, e);
29387         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29388
29389             if(!this.overlay){
29390                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29391                 this.overlay.unselectable();
29392                 this.overlay.enableDisplayMode("block");
29393                 this.overlay.on("mousemove", this.onMouseMove, this);
29394                 this.overlay.on("mouseup", this.onMouseUp, this);
29395             }
29396             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29397
29398             this.resizing = true;
29399             this.startBox = this.el.getBox();
29400             this.startPoint = e.getXY();
29401             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29402                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29403
29404             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29405             this.overlay.show();
29406
29407             if(this.constrainTo) {
29408                 var ct = Roo.get(this.constrainTo);
29409                 this.resizeRegion = ct.getRegion().adjust(
29410                     ct.getFrameWidth('t'),
29411                     ct.getFrameWidth('l'),
29412                     -ct.getFrameWidth('b'),
29413                     -ct.getFrameWidth('r')
29414                 );
29415             }
29416
29417             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29418             this.proxy.show();
29419             this.proxy.setBox(this.startBox);
29420             if(!this.dynamic){
29421                 this.proxy.setStyle('visibility', 'visible');
29422             }
29423         }
29424     },
29425
29426     // private
29427     onMouseDown : function(handle, e){
29428         if(this.enabled){
29429             e.stopEvent();
29430             this.activeHandle = handle;
29431             this.startSizing(e, handle);
29432         }
29433     },
29434
29435     // private
29436     onMouseUp : function(e){
29437         var size = this.resizeElement();
29438         this.resizing = false;
29439         this.handleOut();
29440         this.overlay.hide();
29441         this.proxy.hide();
29442         this.fireEvent("resize", this, size.width, size.height, e);
29443     },
29444
29445     // private
29446     updateChildSize : function(){
29447         
29448         if(this.resizeChild){
29449             var el = this.el;
29450             var child = this.resizeChild;
29451             var adj = this.adjustments;
29452             if(el.dom.offsetWidth){
29453                 var b = el.getSize(true);
29454                 child.setSize(b.width+adj[0], b.height+adj[1]);
29455             }
29456             // Second call here for IE
29457             // The first call enables instant resizing and
29458             // the second call corrects scroll bars if they
29459             // exist
29460             if(Roo.isIE){
29461                 setTimeout(function(){
29462                     if(el.dom.offsetWidth){
29463                         var b = el.getSize(true);
29464                         child.setSize(b.width+adj[0], b.height+adj[1]);
29465                     }
29466                 }, 10);
29467             }
29468         }
29469     },
29470
29471     // private
29472     snap : function(value, inc, min){
29473         if(!inc || !value) return value;
29474         var newValue = value;
29475         var m = value % inc;
29476         if(m > 0){
29477             if(m > (inc/2)){
29478                 newValue = value + (inc-m);
29479             }else{
29480                 newValue = value - m;
29481             }
29482         }
29483         return Math.max(min, newValue);
29484     },
29485
29486     // private
29487     resizeElement : function(){
29488         var box = this.proxy.getBox();
29489         if(this.updateBox){
29490             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29491         }else{
29492             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29493         }
29494         this.updateChildSize();
29495         if(!this.dynamic){
29496             this.proxy.hide();
29497         }
29498         return box;
29499     },
29500
29501     // private
29502     constrain : function(v, diff, m, mx){
29503         if(v - diff < m){
29504             diff = v - m;
29505         }else if(v - diff > mx){
29506             diff = mx - v;
29507         }
29508         return diff;
29509     },
29510
29511     // private
29512     onMouseMove : function(e){
29513         
29514         if(this.enabled){
29515             try{// try catch so if something goes wrong the user doesn't get hung
29516
29517             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29518                 return;
29519             }
29520
29521             //var curXY = this.startPoint;
29522             var curSize = this.curSize || this.startBox;
29523             var x = this.startBox.x, y = this.startBox.y;
29524             var ox = x, oy = y;
29525             var w = curSize.width, h = curSize.height;
29526             var ow = w, oh = h;
29527             var mw = this.minWidth, mh = this.minHeight;
29528             var mxw = this.maxWidth, mxh = this.maxHeight;
29529             var wi = this.widthIncrement;
29530             var hi = this.heightIncrement;
29531
29532             var eventXY = e.getXY();
29533             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29534             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29535
29536             var pos = this.activeHandle.position;
29537
29538             switch(pos){
29539                 case "east":
29540                     w += diffX;
29541                     w = Math.min(Math.max(mw, w), mxw);
29542                     break;
29543              
29544                 case "south":
29545                     h += diffY;
29546                     h = Math.min(Math.max(mh, h), mxh);
29547                     break;
29548                 case "southeast":
29549                     w += diffX;
29550                     h += diffY;
29551                     w = Math.min(Math.max(mw, w), mxw);
29552                     h = Math.min(Math.max(mh, h), mxh);
29553                     break;
29554                 case "north":
29555                     diffY = this.constrain(h, diffY, mh, mxh);
29556                     y += diffY;
29557                     h -= diffY;
29558                     break;
29559                 case "hdrag":
29560                     
29561                     if (wi) {
29562                         var adiffX = Math.abs(diffX);
29563                         var sub = (adiffX % wi); // how much 
29564                         if (sub > (wi/2)) { // far enough to snap
29565                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29566                         } else {
29567                             // remove difference.. 
29568                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29569                         }
29570                     }
29571                     x += diffX;
29572                     x = Math.max(this.minX, x);
29573                     break;
29574                 case "west":
29575                     diffX = this.constrain(w, diffX, mw, mxw);
29576                     x += diffX;
29577                     w -= diffX;
29578                     break;
29579                 case "northeast":
29580                     w += diffX;
29581                     w = Math.min(Math.max(mw, w), mxw);
29582                     diffY = this.constrain(h, diffY, mh, mxh);
29583                     y += diffY;
29584                     h -= diffY;
29585                     break;
29586                 case "northwest":
29587                     diffX = this.constrain(w, diffX, mw, mxw);
29588                     diffY = this.constrain(h, diffY, mh, mxh);
29589                     y += diffY;
29590                     h -= diffY;
29591                     x += diffX;
29592                     w -= diffX;
29593                     break;
29594                case "southwest":
29595                     diffX = this.constrain(w, diffX, mw, mxw);
29596                     h += diffY;
29597                     h = Math.min(Math.max(mh, h), mxh);
29598                     x += diffX;
29599                     w -= diffX;
29600                     break;
29601             }
29602
29603             var sw = this.snap(w, wi, mw);
29604             var sh = this.snap(h, hi, mh);
29605             if(sw != w || sh != h){
29606                 switch(pos){
29607                     case "northeast":
29608                         y -= sh - h;
29609                     break;
29610                     case "north":
29611                         y -= sh - h;
29612                         break;
29613                     case "southwest":
29614                         x -= sw - w;
29615                     break;
29616                     case "west":
29617                         x -= sw - w;
29618                         break;
29619                     case "northwest":
29620                         x -= sw - w;
29621                         y -= sh - h;
29622                     break;
29623                 }
29624                 w = sw;
29625                 h = sh;
29626             }
29627
29628             if(this.preserveRatio){
29629                 switch(pos){
29630                     case "southeast":
29631                     case "east":
29632                         h = oh * (w/ow);
29633                         h = Math.min(Math.max(mh, h), mxh);
29634                         w = ow * (h/oh);
29635                        break;
29636                     case "south":
29637                         w = ow * (h/oh);
29638                         w = Math.min(Math.max(mw, w), mxw);
29639                         h = oh * (w/ow);
29640                         break;
29641                     case "northeast":
29642                         w = ow * (h/oh);
29643                         w = Math.min(Math.max(mw, w), mxw);
29644                         h = oh * (w/ow);
29645                     break;
29646                     case "north":
29647                         var tw = w;
29648                         w = ow * (h/oh);
29649                         w = Math.min(Math.max(mw, w), mxw);
29650                         h = oh * (w/ow);
29651                         x += (tw - w) / 2;
29652                         break;
29653                     case "southwest":
29654                         h = oh * (w/ow);
29655                         h = Math.min(Math.max(mh, h), mxh);
29656                         var tw = w;
29657                         w = ow * (h/oh);
29658                         x += tw - w;
29659                         break;
29660                     case "west":
29661                         var th = h;
29662                         h = oh * (w/ow);
29663                         h = Math.min(Math.max(mh, h), mxh);
29664                         y += (th - h) / 2;
29665                         var tw = w;
29666                         w = ow * (h/oh);
29667                         x += tw - w;
29668                        break;
29669                     case "northwest":
29670                         var tw = w;
29671                         var th = h;
29672                         h = oh * (w/ow);
29673                         h = Math.min(Math.max(mh, h), mxh);
29674                         w = ow * (h/oh);
29675                         y += th - h;
29676                         x += tw - w;
29677                        break;
29678
29679                 }
29680             }
29681             if (pos == 'hdrag') {
29682                 w = ow;
29683             }
29684             this.proxy.setBounds(x, y, w, h);
29685             if(this.dynamic){
29686                 this.resizeElement();
29687             }
29688             }catch(e){}
29689         }
29690         this.fireEvent("resizing", this, x, y, w, h, e);
29691     },
29692
29693     // private
29694     handleOver : function(){
29695         if(this.enabled){
29696             this.el.addClass("x-resizable-over");
29697         }
29698     },
29699
29700     // private
29701     handleOut : function(){
29702         if(!this.resizing){
29703             this.el.removeClass("x-resizable-over");
29704         }
29705     },
29706
29707     /**
29708      * Returns the element this component is bound to.
29709      * @return {Roo.Element}
29710      */
29711     getEl : function(){
29712         return this.el;
29713     },
29714
29715     /**
29716      * Returns the resizeChild element (or null).
29717      * @return {Roo.Element}
29718      */
29719     getResizeChild : function(){
29720         return this.resizeChild;
29721     },
29722     groupHandler : function()
29723     {
29724         
29725     },
29726     /**
29727      * Destroys this resizable. If the element was wrapped and
29728      * removeEl is not true then the element remains.
29729      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29730      */
29731     destroy : function(removeEl){
29732         this.proxy.remove();
29733         if(this.overlay){
29734             this.overlay.removeAllListeners();
29735             this.overlay.remove();
29736         }
29737         var ps = Roo.Resizable.positions;
29738         for(var k in ps){
29739             if(typeof ps[k] != "function" && this[ps[k]]){
29740                 var h = this[ps[k]];
29741                 h.el.removeAllListeners();
29742                 h.el.remove();
29743             }
29744         }
29745         if(removeEl){
29746             this.el.update("");
29747             this.el.remove();
29748         }
29749     }
29750 });
29751
29752 // private
29753 // hash to map config positions to true positions
29754 Roo.Resizable.positions = {
29755     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29756     hd: "hdrag"
29757 };
29758
29759 // private
29760 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29761     if(!this.tpl){
29762         // only initialize the template if resizable is used
29763         var tpl = Roo.DomHelper.createTemplate(
29764             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29765         );
29766         tpl.compile();
29767         Roo.Resizable.Handle.prototype.tpl = tpl;
29768     }
29769     this.position = pos;
29770     this.rz = rz;
29771     // show north drag fro topdra
29772     var handlepos = pos == 'hdrag' ? 'north' : pos;
29773     
29774     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29775     if (pos == 'hdrag') {
29776         this.el.setStyle('cursor', 'pointer');
29777     }
29778     this.el.unselectable();
29779     if(transparent){
29780         this.el.setOpacity(0);
29781     }
29782     this.el.on("mousedown", this.onMouseDown, this);
29783     if(!disableTrackOver){
29784         this.el.on("mouseover", this.onMouseOver, this);
29785         this.el.on("mouseout", this.onMouseOut, this);
29786     }
29787 };
29788
29789 // private
29790 Roo.Resizable.Handle.prototype = {
29791     afterResize : function(rz){
29792         Roo.log('after?');
29793         // do nothing
29794     },
29795     // private
29796     onMouseDown : function(e){
29797         this.rz.onMouseDown(this, e);
29798     },
29799     // private
29800     onMouseOver : function(e){
29801         this.rz.handleOver(this, e);
29802     },
29803     // private
29804     onMouseOut : function(e){
29805         this.rz.handleOut(this, e);
29806     }
29807 };/*
29808  * Based on:
29809  * Ext JS Library 1.1.1
29810  * Copyright(c) 2006-2007, Ext JS, LLC.
29811  *
29812  * Originally Released Under LGPL - original licence link has changed is not relivant.
29813  *
29814  * Fork - LGPL
29815  * <script type="text/javascript">
29816  */
29817
29818 /**
29819  * @class Roo.Editor
29820  * @extends Roo.Component
29821  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29822  * @constructor
29823  * Create a new Editor
29824  * @param {Roo.form.Field} field The Field object (or descendant)
29825  * @param {Object} config The config object
29826  */
29827 Roo.Editor = function(field, config){
29828     Roo.Editor.superclass.constructor.call(this, config);
29829     this.field = field;
29830     this.addEvents({
29831         /**
29832              * @event beforestartedit
29833              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29834              * false from the handler of this event.
29835              * @param {Editor} this
29836              * @param {Roo.Element} boundEl The underlying element bound to this editor
29837              * @param {Mixed} value The field value being set
29838              */
29839         "beforestartedit" : true,
29840         /**
29841              * @event startedit
29842              * Fires when this editor is displayed
29843              * @param {Roo.Element} boundEl The underlying element bound to this editor
29844              * @param {Mixed} value The starting field value
29845              */
29846         "startedit" : true,
29847         /**
29848              * @event beforecomplete
29849              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29850              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29851              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29852              * event will not fire since no edit actually occurred.
29853              * @param {Editor} this
29854              * @param {Mixed} value The current field value
29855              * @param {Mixed} startValue The original field value
29856              */
29857         "beforecomplete" : true,
29858         /**
29859              * @event complete
29860              * Fires after editing is complete and any changed value has been written to the underlying field.
29861              * @param {Editor} this
29862              * @param {Mixed} value The current field value
29863              * @param {Mixed} startValue The original field value
29864              */
29865         "complete" : true,
29866         /**
29867          * @event specialkey
29868          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29869          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29870          * @param {Roo.form.Field} this
29871          * @param {Roo.EventObject} e The event object
29872          */
29873         "specialkey" : true
29874     });
29875 };
29876
29877 Roo.extend(Roo.Editor, Roo.Component, {
29878     /**
29879      * @cfg {Boolean/String} autosize
29880      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29881      * or "height" to adopt the height only (defaults to false)
29882      */
29883     /**
29884      * @cfg {Boolean} revertInvalid
29885      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29886      * validation fails (defaults to true)
29887      */
29888     /**
29889      * @cfg {Boolean} ignoreNoChange
29890      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29891      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29892      * will never be ignored.
29893      */
29894     /**
29895      * @cfg {Boolean} hideEl
29896      * False to keep the bound element visible while the editor is displayed (defaults to true)
29897      */
29898     /**
29899      * @cfg {Mixed} value
29900      * The data value of the underlying field (defaults to "")
29901      */
29902     value : "",
29903     /**
29904      * @cfg {String} alignment
29905      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29906      */
29907     alignment: "c-c?",
29908     /**
29909      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29910      * for bottom-right shadow (defaults to "frame")
29911      */
29912     shadow : "frame",
29913     /**
29914      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29915      */
29916     constrain : false,
29917     /**
29918      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29919      */
29920     completeOnEnter : false,
29921     /**
29922      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29923      */
29924     cancelOnEsc : false,
29925     /**
29926      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29927      */
29928     updateEl : false,
29929
29930     // private
29931     onRender : function(ct, position){
29932         this.el = new Roo.Layer({
29933             shadow: this.shadow,
29934             cls: "x-editor",
29935             parentEl : ct,
29936             shim : this.shim,
29937             shadowOffset:4,
29938             id: this.id,
29939             constrain: this.constrain
29940         });
29941         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29942         if(this.field.msgTarget != 'title'){
29943             this.field.msgTarget = 'qtip';
29944         }
29945         this.field.render(this.el);
29946         if(Roo.isGecko){
29947             this.field.el.dom.setAttribute('autocomplete', 'off');
29948         }
29949         this.field.on("specialkey", this.onSpecialKey, this);
29950         if(this.swallowKeys){
29951             this.field.el.swallowEvent(['keydown','keypress']);
29952         }
29953         this.field.show();
29954         this.field.on("blur", this.onBlur, this);
29955         if(this.field.grow){
29956             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29957         }
29958     },
29959
29960     onSpecialKey : function(field, e)
29961     {
29962         //Roo.log('editor onSpecialKey');
29963         if(this.completeOnEnter && e.getKey() == e.ENTER){
29964             e.stopEvent();
29965             this.completeEdit();
29966             return;
29967         }
29968         // do not fire special key otherwise it might hide close the editor...
29969         if(e.getKey() == e.ENTER){    
29970             return;
29971         }
29972         if(this.cancelOnEsc && e.getKey() == e.ESC){
29973             this.cancelEdit();
29974             return;
29975         } 
29976         this.fireEvent('specialkey', field, e);
29977     
29978     },
29979
29980     /**
29981      * Starts the editing process and shows the editor.
29982      * @param {String/HTMLElement/Element} el The element to edit
29983      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29984       * to the innerHTML of el.
29985      */
29986     startEdit : function(el, value){
29987         if(this.editing){
29988             this.completeEdit();
29989         }
29990         this.boundEl = Roo.get(el);
29991         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29992         if(!this.rendered){
29993             this.render(this.parentEl || document.body);
29994         }
29995         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29996             return;
29997         }
29998         this.startValue = v;
29999         this.field.setValue(v);
30000         if(this.autoSize){
30001             var sz = this.boundEl.getSize();
30002             switch(this.autoSize){
30003                 case "width":
30004                 this.setSize(sz.width,  "");
30005                 break;
30006                 case "height":
30007                 this.setSize("",  sz.height);
30008                 break;
30009                 default:
30010                 this.setSize(sz.width,  sz.height);
30011             }
30012         }
30013         this.el.alignTo(this.boundEl, this.alignment);
30014         this.editing = true;
30015         if(Roo.QuickTips){
30016             Roo.QuickTips.disable();
30017         }
30018         this.show();
30019     },
30020
30021     /**
30022      * Sets the height and width of this editor.
30023      * @param {Number} width The new width
30024      * @param {Number} height The new height
30025      */
30026     setSize : function(w, h){
30027         this.field.setSize(w, h);
30028         if(this.el){
30029             this.el.sync();
30030         }
30031     },
30032
30033     /**
30034      * Realigns the editor to the bound field based on the current alignment config value.
30035      */
30036     realign : function(){
30037         this.el.alignTo(this.boundEl, this.alignment);
30038     },
30039
30040     /**
30041      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30042      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30043      */
30044     completeEdit : function(remainVisible){
30045         if(!this.editing){
30046             return;
30047         }
30048         var v = this.getValue();
30049         if(this.revertInvalid !== false && !this.field.isValid()){
30050             v = this.startValue;
30051             this.cancelEdit(true);
30052         }
30053         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30054             this.editing = false;
30055             this.hide();
30056             return;
30057         }
30058         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30059             this.editing = false;
30060             if(this.updateEl && this.boundEl){
30061                 this.boundEl.update(v);
30062             }
30063             if(remainVisible !== true){
30064                 this.hide();
30065             }
30066             this.fireEvent("complete", this, v, this.startValue);
30067         }
30068     },
30069
30070     // private
30071     onShow : function(){
30072         this.el.show();
30073         if(this.hideEl !== false){
30074             this.boundEl.hide();
30075         }
30076         this.field.show();
30077         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30078             this.fixIEFocus = true;
30079             this.deferredFocus.defer(50, this);
30080         }else{
30081             this.field.focus();
30082         }
30083         this.fireEvent("startedit", this.boundEl, this.startValue);
30084     },
30085
30086     deferredFocus : function(){
30087         if(this.editing){
30088             this.field.focus();
30089         }
30090     },
30091
30092     /**
30093      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30094      * reverted to the original starting value.
30095      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30096      * cancel (defaults to false)
30097      */
30098     cancelEdit : function(remainVisible){
30099         if(this.editing){
30100             this.setValue(this.startValue);
30101             if(remainVisible !== true){
30102                 this.hide();
30103             }
30104         }
30105     },
30106
30107     // private
30108     onBlur : function(){
30109         if(this.allowBlur !== true && this.editing){
30110             this.completeEdit();
30111         }
30112     },
30113
30114     // private
30115     onHide : function(){
30116         if(this.editing){
30117             this.completeEdit();
30118             return;
30119         }
30120         this.field.blur();
30121         if(this.field.collapse){
30122             this.field.collapse();
30123         }
30124         this.el.hide();
30125         if(this.hideEl !== false){
30126             this.boundEl.show();
30127         }
30128         if(Roo.QuickTips){
30129             Roo.QuickTips.enable();
30130         }
30131     },
30132
30133     /**
30134      * Sets the data value of the editor
30135      * @param {Mixed} value Any valid value supported by the underlying field
30136      */
30137     setValue : function(v){
30138         this.field.setValue(v);
30139     },
30140
30141     /**
30142      * Gets the data value of the editor
30143      * @return {Mixed} The data value
30144      */
30145     getValue : function(){
30146         return this.field.getValue();
30147     }
30148 });/*
30149  * Based on:
30150  * Ext JS Library 1.1.1
30151  * Copyright(c) 2006-2007, Ext JS, LLC.
30152  *
30153  * Originally Released Under LGPL - original licence link has changed is not relivant.
30154  *
30155  * Fork - LGPL
30156  * <script type="text/javascript">
30157  */
30158  
30159 /**
30160  * @class Roo.BasicDialog
30161  * @extends Roo.util.Observable
30162  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30163  * <pre><code>
30164 var dlg = new Roo.BasicDialog("my-dlg", {
30165     height: 200,
30166     width: 300,
30167     minHeight: 100,
30168     minWidth: 150,
30169     modal: true,
30170     proxyDrag: true,
30171     shadow: true
30172 });
30173 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30174 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30175 dlg.addButton('Cancel', dlg.hide, dlg);
30176 dlg.show();
30177 </code></pre>
30178   <b>A Dialog should always be a direct child of the body element.</b>
30179  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30180  * @cfg {String} title Default text to display in the title bar (defaults to null)
30181  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30182  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30183  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30184  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30185  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30186  * (defaults to null with no animation)
30187  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30188  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30189  * property for valid values (defaults to 'all')
30190  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30191  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30192  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30193  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30194  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30195  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30196  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30197  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30198  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30199  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30200  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30201  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30202  * draggable = true (defaults to false)
30203  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30204  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30205  * shadow (defaults to false)
30206  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30207  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30208  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30209  * @cfg {Array} buttons Array of buttons
30210  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30211  * @constructor
30212  * Create a new BasicDialog.
30213  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30214  * @param {Object} config Configuration options
30215  */
30216 Roo.BasicDialog = function(el, config){
30217     this.el = Roo.get(el);
30218     var dh = Roo.DomHelper;
30219     if(!this.el && config && config.autoCreate){
30220         if(typeof config.autoCreate == "object"){
30221             if(!config.autoCreate.id){
30222                 config.autoCreate.id = el;
30223             }
30224             this.el = dh.append(document.body,
30225                         config.autoCreate, true);
30226         }else{
30227             this.el = dh.append(document.body,
30228                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30229         }
30230     }
30231     el = this.el;
30232     el.setDisplayed(true);
30233     el.hide = this.hideAction;
30234     this.id = el.id;
30235     el.addClass("x-dlg");
30236
30237     Roo.apply(this, config);
30238
30239     this.proxy = el.createProxy("x-dlg-proxy");
30240     this.proxy.hide = this.hideAction;
30241     this.proxy.setOpacity(.5);
30242     this.proxy.hide();
30243
30244     if(config.width){
30245         el.setWidth(config.width);
30246     }
30247     if(config.height){
30248         el.setHeight(config.height);
30249     }
30250     this.size = el.getSize();
30251     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30252         this.xy = [config.x,config.y];
30253     }else{
30254         this.xy = el.getCenterXY(true);
30255     }
30256     /** The header element @type Roo.Element */
30257     this.header = el.child("> .x-dlg-hd");
30258     /** The body element @type Roo.Element */
30259     this.body = el.child("> .x-dlg-bd");
30260     /** The footer element @type Roo.Element */
30261     this.footer = el.child("> .x-dlg-ft");
30262
30263     if(!this.header){
30264         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30265     }
30266     if(!this.body){
30267         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30268     }
30269
30270     this.header.unselectable();
30271     if(this.title){
30272         this.header.update(this.title);
30273     }
30274     // this element allows the dialog to be focused for keyboard event
30275     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30276     this.focusEl.swallowEvent("click", true);
30277
30278     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30279
30280     // wrap the body and footer for special rendering
30281     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30282     if(this.footer){
30283         this.bwrap.dom.appendChild(this.footer.dom);
30284     }
30285
30286     this.bg = this.el.createChild({
30287         tag: "div", cls:"x-dlg-bg",
30288         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30289     });
30290     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30291
30292
30293     if(this.autoScroll !== false && !this.autoTabs){
30294         this.body.setStyle("overflow", "auto");
30295     }
30296
30297     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30298
30299     if(this.closable !== false){
30300         this.el.addClass("x-dlg-closable");
30301         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30302         this.close.on("click", this.closeClick, this);
30303         this.close.addClassOnOver("x-dlg-close-over");
30304     }
30305     if(this.collapsible !== false){
30306         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30307         this.collapseBtn.on("click", this.collapseClick, this);
30308         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30309         this.header.on("dblclick", this.collapseClick, this);
30310     }
30311     if(this.resizable !== false){
30312         this.el.addClass("x-dlg-resizable");
30313         this.resizer = new Roo.Resizable(el, {
30314             minWidth: this.minWidth || 80,
30315             minHeight:this.minHeight || 80,
30316             handles: this.resizeHandles || "all",
30317             pinned: true
30318         });
30319         this.resizer.on("beforeresize", this.beforeResize, this);
30320         this.resizer.on("resize", this.onResize, this);
30321     }
30322     if(this.draggable !== false){
30323         el.addClass("x-dlg-draggable");
30324         if (!this.proxyDrag) {
30325             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30326         }
30327         else {
30328             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30329         }
30330         dd.setHandleElId(this.header.id);
30331         dd.endDrag = this.endMove.createDelegate(this);
30332         dd.startDrag = this.startMove.createDelegate(this);
30333         dd.onDrag = this.onDrag.createDelegate(this);
30334         dd.scroll = false;
30335         this.dd = dd;
30336     }
30337     if(this.modal){
30338         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30339         this.mask.enableDisplayMode("block");
30340         this.mask.hide();
30341         this.el.addClass("x-dlg-modal");
30342     }
30343     if(this.shadow){
30344         this.shadow = new Roo.Shadow({
30345             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30346             offset : this.shadowOffset
30347         });
30348     }else{
30349         this.shadowOffset = 0;
30350     }
30351     if(Roo.useShims && this.shim !== false){
30352         this.shim = this.el.createShim();
30353         this.shim.hide = this.hideAction;
30354         this.shim.hide();
30355     }else{
30356         this.shim = false;
30357     }
30358     if(this.autoTabs){
30359         this.initTabs();
30360     }
30361     if (this.buttons) { 
30362         var bts= this.buttons;
30363         this.buttons = [];
30364         Roo.each(bts, function(b) {
30365             this.addButton(b);
30366         }, this);
30367     }
30368     
30369     
30370     this.addEvents({
30371         /**
30372          * @event keydown
30373          * Fires when a key is pressed
30374          * @param {Roo.BasicDialog} this
30375          * @param {Roo.EventObject} e
30376          */
30377         "keydown" : true,
30378         /**
30379          * @event move
30380          * Fires when this dialog is moved by the user.
30381          * @param {Roo.BasicDialog} this
30382          * @param {Number} x The new page X
30383          * @param {Number} y The new page Y
30384          */
30385         "move" : true,
30386         /**
30387          * @event resize
30388          * Fires when this dialog is resized by the user.
30389          * @param {Roo.BasicDialog} this
30390          * @param {Number} width The new width
30391          * @param {Number} height The new height
30392          */
30393         "resize" : true,
30394         /**
30395          * @event beforehide
30396          * Fires before this dialog is hidden.
30397          * @param {Roo.BasicDialog} this
30398          */
30399         "beforehide" : true,
30400         /**
30401          * @event hide
30402          * Fires when this dialog is hidden.
30403          * @param {Roo.BasicDialog} this
30404          */
30405         "hide" : true,
30406         /**
30407          * @event beforeshow
30408          * Fires before this dialog is shown.
30409          * @param {Roo.BasicDialog} this
30410          */
30411         "beforeshow" : true,
30412         /**
30413          * @event show
30414          * Fires when this dialog is shown.
30415          * @param {Roo.BasicDialog} this
30416          */
30417         "show" : true
30418     });
30419     el.on("keydown", this.onKeyDown, this);
30420     el.on("mousedown", this.toFront, this);
30421     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30422     this.el.hide();
30423     Roo.DialogManager.register(this);
30424     Roo.BasicDialog.superclass.constructor.call(this);
30425 };
30426
30427 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30428     shadowOffset: Roo.isIE ? 6 : 5,
30429     minHeight: 80,
30430     minWidth: 200,
30431     minButtonWidth: 75,
30432     defaultButton: null,
30433     buttonAlign: "right",
30434     tabTag: 'div',
30435     firstShow: true,
30436
30437     /**
30438      * Sets the dialog title text
30439      * @param {String} text The title text to display
30440      * @return {Roo.BasicDialog} this
30441      */
30442     setTitle : function(text){
30443         this.header.update(text);
30444         return this;
30445     },
30446
30447     // private
30448     closeClick : function(){
30449         this.hide();
30450     },
30451
30452     // private
30453     collapseClick : function(){
30454         this[this.collapsed ? "expand" : "collapse"]();
30455     },
30456
30457     /**
30458      * Collapses the dialog to its minimized state (only the title bar is visible).
30459      * Equivalent to the user clicking the collapse dialog button.
30460      */
30461     collapse : function(){
30462         if(!this.collapsed){
30463             this.collapsed = true;
30464             this.el.addClass("x-dlg-collapsed");
30465             this.restoreHeight = this.el.getHeight();
30466             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30467         }
30468     },
30469
30470     /**
30471      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30472      * clicking the expand dialog button.
30473      */
30474     expand : function(){
30475         if(this.collapsed){
30476             this.collapsed = false;
30477             this.el.removeClass("x-dlg-collapsed");
30478             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30479         }
30480     },
30481
30482     /**
30483      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30484      * @return {Roo.TabPanel} The tabs component
30485      */
30486     initTabs : function(){
30487         var tabs = this.getTabs();
30488         while(tabs.getTab(0)){
30489             tabs.removeTab(0);
30490         }
30491         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30492             var dom = el.dom;
30493             tabs.addTab(Roo.id(dom), dom.title);
30494             dom.title = "";
30495         });
30496         tabs.activate(0);
30497         return tabs;
30498     },
30499
30500     // private
30501     beforeResize : function(){
30502         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30503     },
30504
30505     // private
30506     onResize : function(){
30507         this.refreshSize();
30508         this.syncBodyHeight();
30509         this.adjustAssets();
30510         this.focus();
30511         this.fireEvent("resize", this, this.size.width, this.size.height);
30512     },
30513
30514     // private
30515     onKeyDown : function(e){
30516         if(this.isVisible()){
30517             this.fireEvent("keydown", this, e);
30518         }
30519     },
30520
30521     /**
30522      * Resizes the dialog.
30523      * @param {Number} width
30524      * @param {Number} height
30525      * @return {Roo.BasicDialog} this
30526      */
30527     resizeTo : function(width, height){
30528         this.el.setSize(width, height);
30529         this.size = {width: width, height: height};
30530         this.syncBodyHeight();
30531         if(this.fixedcenter){
30532             this.center();
30533         }
30534         if(this.isVisible()){
30535             this.constrainXY();
30536             this.adjustAssets();
30537         }
30538         this.fireEvent("resize", this, width, height);
30539         return this;
30540     },
30541
30542
30543     /**
30544      * Resizes the dialog to fit the specified content size.
30545      * @param {Number} width
30546      * @param {Number} height
30547      * @return {Roo.BasicDialog} this
30548      */
30549     setContentSize : function(w, h){
30550         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30551         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30552         //if(!this.el.isBorderBox()){
30553             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30554             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30555         //}
30556         if(this.tabs){
30557             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30558             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30559         }
30560         this.resizeTo(w, h);
30561         return this;
30562     },
30563
30564     /**
30565      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30566      * executed in response to a particular key being pressed while the dialog is active.
30567      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30568      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30569      * @param {Function} fn The function to call
30570      * @param {Object} scope (optional) The scope of the function
30571      * @return {Roo.BasicDialog} this
30572      */
30573     addKeyListener : function(key, fn, scope){
30574         var keyCode, shift, ctrl, alt;
30575         if(typeof key == "object" && !(key instanceof Array)){
30576             keyCode = key["key"];
30577             shift = key["shift"];
30578             ctrl = key["ctrl"];
30579             alt = key["alt"];
30580         }else{
30581             keyCode = key;
30582         }
30583         var handler = function(dlg, e){
30584             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30585                 var k = e.getKey();
30586                 if(keyCode instanceof Array){
30587                     for(var i = 0, len = keyCode.length; i < len; i++){
30588                         if(keyCode[i] == k){
30589                           fn.call(scope || window, dlg, k, e);
30590                           return;
30591                         }
30592                     }
30593                 }else{
30594                     if(k == keyCode){
30595                         fn.call(scope || window, dlg, k, e);
30596                     }
30597                 }
30598             }
30599         };
30600         this.on("keydown", handler);
30601         return this;
30602     },
30603
30604     /**
30605      * Returns the TabPanel component (creates it if it doesn't exist).
30606      * Note: If you wish to simply check for the existence of tabs without creating them,
30607      * check for a null 'tabs' property.
30608      * @return {Roo.TabPanel} The tabs component
30609      */
30610     getTabs : function(){
30611         if(!this.tabs){
30612             this.el.addClass("x-dlg-auto-tabs");
30613             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30614             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30615         }
30616         return this.tabs;
30617     },
30618
30619     /**
30620      * Adds a button to the footer section of the dialog.
30621      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30622      * object or a valid Roo.DomHelper element config
30623      * @param {Function} handler The function called when the button is clicked
30624      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30625      * @return {Roo.Button} The new button
30626      */
30627     addButton : function(config, handler, scope){
30628         var dh = Roo.DomHelper;
30629         if(!this.footer){
30630             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30631         }
30632         if(!this.btnContainer){
30633             var tb = this.footer.createChild({
30634
30635                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30636                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30637             }, null, true);
30638             this.btnContainer = tb.firstChild.firstChild.firstChild;
30639         }
30640         var bconfig = {
30641             handler: handler,
30642             scope: scope,
30643             minWidth: this.minButtonWidth,
30644             hideParent:true
30645         };
30646         if(typeof config == "string"){
30647             bconfig.text = config;
30648         }else{
30649             if(config.tag){
30650                 bconfig.dhconfig = config;
30651             }else{
30652                 Roo.apply(bconfig, config);
30653             }
30654         }
30655         var fc = false;
30656         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30657             bconfig.position = Math.max(0, bconfig.position);
30658             fc = this.btnContainer.childNodes[bconfig.position];
30659         }
30660          
30661         var btn = new Roo.Button(
30662             fc ? 
30663                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30664                 : this.btnContainer.appendChild(document.createElement("td")),
30665             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30666             bconfig
30667         );
30668         this.syncBodyHeight();
30669         if(!this.buttons){
30670             /**
30671              * Array of all the buttons that have been added to this dialog via addButton
30672              * @type Array
30673              */
30674             this.buttons = [];
30675         }
30676         this.buttons.push(btn);
30677         return btn;
30678     },
30679
30680     /**
30681      * Sets the default button to be focused when the dialog is displayed.
30682      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30683      * @return {Roo.BasicDialog} this
30684      */
30685     setDefaultButton : function(btn){
30686         this.defaultButton = btn;
30687         return this;
30688     },
30689
30690     // private
30691     getHeaderFooterHeight : function(safe){
30692         var height = 0;
30693         if(this.header){
30694            height += this.header.getHeight();
30695         }
30696         if(this.footer){
30697            var fm = this.footer.getMargins();
30698             height += (this.footer.getHeight()+fm.top+fm.bottom);
30699         }
30700         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30701         height += this.centerBg.getPadding("tb");
30702         return height;
30703     },
30704
30705     // private
30706     syncBodyHeight : function()
30707     {
30708         var bd = this.body, // the text
30709             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30710             bw = this.bwrap;
30711         var height = this.size.height - this.getHeaderFooterHeight(false);
30712         bd.setHeight(height-bd.getMargins("tb"));
30713         var hh = this.header.getHeight();
30714         var h = this.size.height-hh;
30715         cb.setHeight(h);
30716         
30717         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30718         bw.setHeight(h-cb.getPadding("tb"));
30719         
30720         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30721         bd.setWidth(bw.getWidth(true));
30722         if(this.tabs){
30723             this.tabs.syncHeight();
30724             if(Roo.isIE){
30725                 this.tabs.el.repaint();
30726             }
30727         }
30728     },
30729
30730     /**
30731      * Restores the previous state of the dialog if Roo.state is configured.
30732      * @return {Roo.BasicDialog} this
30733      */
30734     restoreState : function(){
30735         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30736         if(box && box.width){
30737             this.xy = [box.x, box.y];
30738             this.resizeTo(box.width, box.height);
30739         }
30740         return this;
30741     },
30742
30743     // private
30744     beforeShow : function(){
30745         this.expand();
30746         if(this.fixedcenter){
30747             this.xy = this.el.getCenterXY(true);
30748         }
30749         if(this.modal){
30750             Roo.get(document.body).addClass("x-body-masked");
30751             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30752             this.mask.show();
30753         }
30754         this.constrainXY();
30755     },
30756
30757     // private
30758     animShow : function(){
30759         var b = Roo.get(this.animateTarget).getBox();
30760         this.proxy.setSize(b.width, b.height);
30761         this.proxy.setLocation(b.x, b.y);
30762         this.proxy.show();
30763         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30764                     true, .35, this.showEl.createDelegate(this));
30765     },
30766
30767     /**
30768      * Shows the dialog.
30769      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30770      * @return {Roo.BasicDialog} this
30771      */
30772     show : function(animateTarget){
30773         if (this.fireEvent("beforeshow", this) === false){
30774             return;
30775         }
30776         if(this.syncHeightBeforeShow){
30777             this.syncBodyHeight();
30778         }else if(this.firstShow){
30779             this.firstShow = false;
30780             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30781         }
30782         this.animateTarget = animateTarget || this.animateTarget;
30783         if(!this.el.isVisible()){
30784             this.beforeShow();
30785             if(this.animateTarget && Roo.get(this.animateTarget)){
30786                 this.animShow();
30787             }else{
30788                 this.showEl();
30789             }
30790         }
30791         return this;
30792     },
30793
30794     // private
30795     showEl : function(){
30796         this.proxy.hide();
30797         this.el.setXY(this.xy);
30798         this.el.show();
30799         this.adjustAssets(true);
30800         this.toFront();
30801         this.focus();
30802         // IE peekaboo bug - fix found by Dave Fenwick
30803         if(Roo.isIE){
30804             this.el.repaint();
30805         }
30806         this.fireEvent("show", this);
30807     },
30808
30809     /**
30810      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30811      * dialog itself will receive focus.
30812      */
30813     focus : function(){
30814         if(this.defaultButton){
30815             this.defaultButton.focus();
30816         }else{
30817             this.focusEl.focus();
30818         }
30819     },
30820
30821     // private
30822     constrainXY : function(){
30823         if(this.constraintoviewport !== false){
30824             if(!this.viewSize){
30825                 if(this.container){
30826                     var s = this.container.getSize();
30827                     this.viewSize = [s.width, s.height];
30828                 }else{
30829                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30830                 }
30831             }
30832             var s = Roo.get(this.container||document).getScroll();
30833
30834             var x = this.xy[0], y = this.xy[1];
30835             var w = this.size.width, h = this.size.height;
30836             var vw = this.viewSize[0], vh = this.viewSize[1];
30837             // only move it if it needs it
30838             var moved = false;
30839             // first validate right/bottom
30840             if(x + w > vw+s.left){
30841                 x = vw - w;
30842                 moved = true;
30843             }
30844             if(y + h > vh+s.top){
30845                 y = vh - h;
30846                 moved = true;
30847             }
30848             // then make sure top/left isn't negative
30849             if(x < s.left){
30850                 x = s.left;
30851                 moved = true;
30852             }
30853             if(y < s.top){
30854                 y = s.top;
30855                 moved = true;
30856             }
30857             if(moved){
30858                 // cache xy
30859                 this.xy = [x, y];
30860                 if(this.isVisible()){
30861                     this.el.setLocation(x, y);
30862                     this.adjustAssets();
30863                 }
30864             }
30865         }
30866     },
30867
30868     // private
30869     onDrag : function(){
30870         if(!this.proxyDrag){
30871             this.xy = this.el.getXY();
30872             this.adjustAssets();
30873         }
30874     },
30875
30876     // private
30877     adjustAssets : function(doShow){
30878         var x = this.xy[0], y = this.xy[1];
30879         var w = this.size.width, h = this.size.height;
30880         if(doShow === true){
30881             if(this.shadow){
30882                 this.shadow.show(this.el);
30883             }
30884             if(this.shim){
30885                 this.shim.show();
30886             }
30887         }
30888         if(this.shadow && this.shadow.isVisible()){
30889             this.shadow.show(this.el);
30890         }
30891         if(this.shim && this.shim.isVisible()){
30892             this.shim.setBounds(x, y, w, h);
30893         }
30894     },
30895
30896     // private
30897     adjustViewport : function(w, h){
30898         if(!w || !h){
30899             w = Roo.lib.Dom.getViewWidth();
30900             h = Roo.lib.Dom.getViewHeight();
30901         }
30902         // cache the size
30903         this.viewSize = [w, h];
30904         if(this.modal && this.mask.isVisible()){
30905             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30906             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30907         }
30908         if(this.isVisible()){
30909             this.constrainXY();
30910         }
30911     },
30912
30913     /**
30914      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30915      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30916      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30917      */
30918     destroy : function(removeEl){
30919         if(this.isVisible()){
30920             this.animateTarget = null;
30921             this.hide();
30922         }
30923         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30924         if(this.tabs){
30925             this.tabs.destroy(removeEl);
30926         }
30927         Roo.destroy(
30928              this.shim,
30929              this.proxy,
30930              this.resizer,
30931              this.close,
30932              this.mask
30933         );
30934         if(this.dd){
30935             this.dd.unreg();
30936         }
30937         if(this.buttons){
30938            for(var i = 0, len = this.buttons.length; i < len; i++){
30939                this.buttons[i].destroy();
30940            }
30941         }
30942         this.el.removeAllListeners();
30943         if(removeEl === true){
30944             this.el.update("");
30945             this.el.remove();
30946         }
30947         Roo.DialogManager.unregister(this);
30948     },
30949
30950     // private
30951     startMove : function(){
30952         if(this.proxyDrag){
30953             this.proxy.show();
30954         }
30955         if(this.constraintoviewport !== false){
30956             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30957         }
30958     },
30959
30960     // private
30961     endMove : function(){
30962         if(!this.proxyDrag){
30963             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30964         }else{
30965             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30966             this.proxy.hide();
30967         }
30968         this.refreshSize();
30969         this.adjustAssets();
30970         this.focus();
30971         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30972     },
30973
30974     /**
30975      * Brings this dialog to the front of any other visible dialogs
30976      * @return {Roo.BasicDialog} this
30977      */
30978     toFront : function(){
30979         Roo.DialogManager.bringToFront(this);
30980         return this;
30981     },
30982
30983     /**
30984      * Sends this dialog to the back (under) of any other visible dialogs
30985      * @return {Roo.BasicDialog} this
30986      */
30987     toBack : function(){
30988         Roo.DialogManager.sendToBack(this);
30989         return this;
30990     },
30991
30992     /**
30993      * Centers this dialog in the viewport
30994      * @return {Roo.BasicDialog} this
30995      */
30996     center : function(){
30997         var xy = this.el.getCenterXY(true);
30998         this.moveTo(xy[0], xy[1]);
30999         return this;
31000     },
31001
31002     /**
31003      * Moves the dialog's top-left corner to the specified point
31004      * @param {Number} x
31005      * @param {Number} y
31006      * @return {Roo.BasicDialog} this
31007      */
31008     moveTo : function(x, y){
31009         this.xy = [x,y];
31010         if(this.isVisible()){
31011             this.el.setXY(this.xy);
31012             this.adjustAssets();
31013         }
31014         return this;
31015     },
31016
31017     /**
31018      * Aligns the dialog to the specified element
31019      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31020      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31021      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31022      * @return {Roo.BasicDialog} this
31023      */
31024     alignTo : function(element, position, offsets){
31025         this.xy = this.el.getAlignToXY(element, position, offsets);
31026         if(this.isVisible()){
31027             this.el.setXY(this.xy);
31028             this.adjustAssets();
31029         }
31030         return this;
31031     },
31032
31033     /**
31034      * Anchors an element to another element and realigns it when the window is resized.
31035      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31036      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31037      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31038      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31039      * is a number, it is used as the buffer delay (defaults to 50ms).
31040      * @return {Roo.BasicDialog} this
31041      */
31042     anchorTo : function(el, alignment, offsets, monitorScroll){
31043         var action = function(){
31044             this.alignTo(el, alignment, offsets);
31045         };
31046         Roo.EventManager.onWindowResize(action, this);
31047         var tm = typeof monitorScroll;
31048         if(tm != 'undefined'){
31049             Roo.EventManager.on(window, 'scroll', action, this,
31050                 {buffer: tm == 'number' ? monitorScroll : 50});
31051         }
31052         action.call(this);
31053         return this;
31054     },
31055
31056     /**
31057      * Returns true if the dialog is visible
31058      * @return {Boolean}
31059      */
31060     isVisible : function(){
31061         return this.el.isVisible();
31062     },
31063
31064     // private
31065     animHide : function(callback){
31066         var b = Roo.get(this.animateTarget).getBox();
31067         this.proxy.show();
31068         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31069         this.el.hide();
31070         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31071                     this.hideEl.createDelegate(this, [callback]));
31072     },
31073
31074     /**
31075      * Hides the dialog.
31076      * @param {Function} callback (optional) Function to call when the dialog is hidden
31077      * @return {Roo.BasicDialog} this
31078      */
31079     hide : function(callback){
31080         if (this.fireEvent("beforehide", this) === false){
31081             return;
31082         }
31083         if(this.shadow){
31084             this.shadow.hide();
31085         }
31086         if(this.shim) {
31087           this.shim.hide();
31088         }
31089         // sometimes animateTarget seems to get set.. causing problems...
31090         // this just double checks..
31091         if(this.animateTarget && Roo.get(this.animateTarget)) {
31092            this.animHide(callback);
31093         }else{
31094             this.el.hide();
31095             this.hideEl(callback);
31096         }
31097         return this;
31098     },
31099
31100     // private
31101     hideEl : function(callback){
31102         this.proxy.hide();
31103         if(this.modal){
31104             this.mask.hide();
31105             Roo.get(document.body).removeClass("x-body-masked");
31106         }
31107         this.fireEvent("hide", this);
31108         if(typeof callback == "function"){
31109             callback();
31110         }
31111     },
31112
31113     // private
31114     hideAction : function(){
31115         this.setLeft("-10000px");
31116         this.setTop("-10000px");
31117         this.setStyle("visibility", "hidden");
31118     },
31119
31120     // private
31121     refreshSize : function(){
31122         this.size = this.el.getSize();
31123         this.xy = this.el.getXY();
31124         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31125     },
31126
31127     // private
31128     // z-index is managed by the DialogManager and may be overwritten at any time
31129     setZIndex : function(index){
31130         if(this.modal){
31131             this.mask.setStyle("z-index", index);
31132         }
31133         if(this.shim){
31134             this.shim.setStyle("z-index", ++index);
31135         }
31136         if(this.shadow){
31137             this.shadow.setZIndex(++index);
31138         }
31139         this.el.setStyle("z-index", ++index);
31140         if(this.proxy){
31141             this.proxy.setStyle("z-index", ++index);
31142         }
31143         if(this.resizer){
31144             this.resizer.proxy.setStyle("z-index", ++index);
31145         }
31146
31147         this.lastZIndex = index;
31148     },
31149
31150     /**
31151      * Returns the element for this dialog
31152      * @return {Roo.Element} The underlying dialog Element
31153      */
31154     getEl : function(){
31155         return this.el;
31156     }
31157 });
31158
31159 /**
31160  * @class Roo.DialogManager
31161  * Provides global access to BasicDialogs that have been created and
31162  * support for z-indexing (layering) multiple open dialogs.
31163  */
31164 Roo.DialogManager = function(){
31165     var list = {};
31166     var accessList = [];
31167     var front = null;
31168
31169     // private
31170     var sortDialogs = function(d1, d2){
31171         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31172     };
31173
31174     // private
31175     var orderDialogs = function(){
31176         accessList.sort(sortDialogs);
31177         var seed = Roo.DialogManager.zseed;
31178         for(var i = 0, len = accessList.length; i < len; i++){
31179             var dlg = accessList[i];
31180             if(dlg){
31181                 dlg.setZIndex(seed + (i*10));
31182             }
31183         }
31184     };
31185
31186     return {
31187         /**
31188          * The starting z-index for BasicDialogs (defaults to 9000)
31189          * @type Number The z-index value
31190          */
31191         zseed : 9000,
31192
31193         // private
31194         register : function(dlg){
31195             list[dlg.id] = dlg;
31196             accessList.push(dlg);
31197         },
31198
31199         // private
31200         unregister : function(dlg){
31201             delete list[dlg.id];
31202             var i=0;
31203             var len=0;
31204             if(!accessList.indexOf){
31205                 for(  i = 0, len = accessList.length; i < len; i++){
31206                     if(accessList[i] == dlg){
31207                         accessList.splice(i, 1);
31208                         return;
31209                     }
31210                 }
31211             }else{
31212                  i = accessList.indexOf(dlg);
31213                 if(i != -1){
31214                     accessList.splice(i, 1);
31215                 }
31216             }
31217         },
31218
31219         /**
31220          * Gets a registered dialog by id
31221          * @param {String/Object} id The id of the dialog or a dialog
31222          * @return {Roo.BasicDialog} this
31223          */
31224         get : function(id){
31225             return typeof id == "object" ? id : list[id];
31226         },
31227
31228         /**
31229          * Brings the specified dialog to the front
31230          * @param {String/Object} dlg The id of the dialog or a dialog
31231          * @return {Roo.BasicDialog} this
31232          */
31233         bringToFront : function(dlg){
31234             dlg = this.get(dlg);
31235             if(dlg != front){
31236                 front = dlg;
31237                 dlg._lastAccess = new Date().getTime();
31238                 orderDialogs();
31239             }
31240             return dlg;
31241         },
31242
31243         /**
31244          * Sends the specified dialog to the back
31245          * @param {String/Object} dlg The id of the dialog or a dialog
31246          * @return {Roo.BasicDialog} this
31247          */
31248         sendToBack : function(dlg){
31249             dlg = this.get(dlg);
31250             dlg._lastAccess = -(new Date().getTime());
31251             orderDialogs();
31252             return dlg;
31253         },
31254
31255         /**
31256          * Hides all dialogs
31257          */
31258         hideAll : function(){
31259             for(var id in list){
31260                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31261                     list[id].hide();
31262                 }
31263             }
31264         }
31265     };
31266 }();
31267
31268 /**
31269  * @class Roo.LayoutDialog
31270  * @extends Roo.BasicDialog
31271  * Dialog which provides adjustments for working with a layout in a Dialog.
31272  * Add your necessary layout config options to the dialog's config.<br>
31273  * Example usage (including a nested layout):
31274  * <pre><code>
31275 if(!dialog){
31276     dialog = new Roo.LayoutDialog("download-dlg", {
31277         modal: true,
31278         width:600,
31279         height:450,
31280         shadow:true,
31281         minWidth:500,
31282         minHeight:350,
31283         autoTabs:true,
31284         proxyDrag:true,
31285         // layout config merges with the dialog config
31286         center:{
31287             tabPosition: "top",
31288             alwaysShowTabs: true
31289         }
31290     });
31291     dialog.addKeyListener(27, dialog.hide, dialog);
31292     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31293     dialog.addButton("Build It!", this.getDownload, this);
31294
31295     // we can even add nested layouts
31296     var innerLayout = new Roo.BorderLayout("dl-inner", {
31297         east: {
31298             initialSize: 200,
31299             autoScroll:true,
31300             split:true
31301         },
31302         center: {
31303             autoScroll:true
31304         }
31305     });
31306     innerLayout.beginUpdate();
31307     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31308     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31309     innerLayout.endUpdate(true);
31310
31311     var layout = dialog.getLayout();
31312     layout.beginUpdate();
31313     layout.add("center", new Roo.ContentPanel("standard-panel",
31314                         {title: "Download the Source", fitToFrame:true}));
31315     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31316                {title: "Build your own roo.js"}));
31317     layout.getRegion("center").showPanel(sp);
31318     layout.endUpdate();
31319 }
31320 </code></pre>
31321     * @constructor
31322     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31323     * @param {Object} config configuration options
31324   */
31325 Roo.LayoutDialog = function(el, cfg){
31326     
31327     var config=  cfg;
31328     if (typeof(cfg) == 'undefined') {
31329         config = Roo.apply({}, el);
31330         // not sure why we use documentElement here.. - it should always be body.
31331         // IE7 borks horribly if we use documentElement.
31332         // webkit also does not like documentElement - it creates a body element...
31333         el = Roo.get( document.body || document.documentElement ).createChild();
31334         //config.autoCreate = true;
31335     }
31336     
31337     
31338     config.autoTabs = false;
31339     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31340     this.body.setStyle({overflow:"hidden", position:"relative"});
31341     this.layout = new Roo.BorderLayout(this.body.dom, config);
31342     this.layout.monitorWindowResize = false;
31343     this.el.addClass("x-dlg-auto-layout");
31344     // fix case when center region overwrites center function
31345     this.center = Roo.BasicDialog.prototype.center;
31346     this.on("show", this.layout.layout, this.layout, true);
31347     if (config.items) {
31348         var xitems = config.items;
31349         delete config.items;
31350         Roo.each(xitems, this.addxtype, this);
31351     }
31352     
31353     
31354 };
31355 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31356     /**
31357      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31358      * @deprecated
31359      */
31360     endUpdate : function(){
31361         this.layout.endUpdate();
31362     },
31363
31364     /**
31365      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31366      *  @deprecated
31367      */
31368     beginUpdate : function(){
31369         this.layout.beginUpdate();
31370     },
31371
31372     /**
31373      * Get the BorderLayout for this dialog
31374      * @return {Roo.BorderLayout}
31375      */
31376     getLayout : function(){
31377         return this.layout;
31378     },
31379
31380     showEl : function(){
31381         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31382         if(Roo.isIE7){
31383             this.layout.layout();
31384         }
31385     },
31386
31387     // private
31388     // Use the syncHeightBeforeShow config option to control this automatically
31389     syncBodyHeight : function(){
31390         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31391         if(this.layout){this.layout.layout();}
31392     },
31393     
31394       /**
31395      * Add an xtype element (actually adds to the layout.)
31396      * @return {Object} xdata xtype object data.
31397      */
31398     
31399     addxtype : function(c) {
31400         return this.layout.addxtype(c);
31401     }
31402 });/*
31403  * Based on:
31404  * Ext JS Library 1.1.1
31405  * Copyright(c) 2006-2007, Ext JS, LLC.
31406  *
31407  * Originally Released Under LGPL - original licence link has changed is not relivant.
31408  *
31409  * Fork - LGPL
31410  * <script type="text/javascript">
31411  */
31412  
31413 /**
31414  * @class Roo.MessageBox
31415  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31416  * Example usage:
31417  *<pre><code>
31418 // Basic alert:
31419 Roo.Msg.alert('Status', 'Changes saved successfully.');
31420
31421 // Prompt for user data:
31422 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31423     if (btn == 'ok'){
31424         // process text value...
31425     }
31426 });
31427
31428 // Show a dialog using config options:
31429 Roo.Msg.show({
31430    title:'Save Changes?',
31431    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31432    buttons: Roo.Msg.YESNOCANCEL,
31433    fn: processResult,
31434    animEl: 'elId'
31435 });
31436 </code></pre>
31437  * @singleton
31438  */
31439 Roo.MessageBox = function(){
31440     var dlg, opt, mask, waitTimer;
31441     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31442     var buttons, activeTextEl, bwidth;
31443
31444     // private
31445     var handleButton = function(button){
31446         dlg.hide();
31447         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31448     };
31449
31450     // private
31451     var handleHide = function(){
31452         if(opt && opt.cls){
31453             dlg.el.removeClass(opt.cls);
31454         }
31455         if(waitTimer){
31456             Roo.TaskMgr.stop(waitTimer);
31457             waitTimer = null;
31458         }
31459     };
31460
31461     // private
31462     var updateButtons = function(b){
31463         var width = 0;
31464         if(!b){
31465             buttons["ok"].hide();
31466             buttons["cancel"].hide();
31467             buttons["yes"].hide();
31468             buttons["no"].hide();
31469             dlg.footer.dom.style.display = 'none';
31470             return width;
31471         }
31472         dlg.footer.dom.style.display = '';
31473         for(var k in buttons){
31474             if(typeof buttons[k] != "function"){
31475                 if(b[k]){
31476                     buttons[k].show();
31477                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31478                     width += buttons[k].el.getWidth()+15;
31479                 }else{
31480                     buttons[k].hide();
31481                 }
31482             }
31483         }
31484         return width;
31485     };
31486
31487     // private
31488     var handleEsc = function(d, k, e){
31489         if(opt && opt.closable !== false){
31490             dlg.hide();
31491         }
31492         if(e){
31493             e.stopEvent();
31494         }
31495     };
31496
31497     return {
31498         /**
31499          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31500          * @return {Roo.BasicDialog} The BasicDialog element
31501          */
31502         getDialog : function(){
31503            if(!dlg){
31504                 dlg = new Roo.BasicDialog("x-msg-box", {
31505                     autoCreate : true,
31506                     shadow: true,
31507                     draggable: true,
31508                     resizable:false,
31509                     constraintoviewport:false,
31510                     fixedcenter:true,
31511                     collapsible : false,
31512                     shim:true,
31513                     modal: true,
31514                     width:400, height:100,
31515                     buttonAlign:"center",
31516                     closeClick : function(){
31517                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31518                             handleButton("no");
31519                         }else{
31520                             handleButton("cancel");
31521                         }
31522                     }
31523                 });
31524                 dlg.on("hide", handleHide);
31525                 mask = dlg.mask;
31526                 dlg.addKeyListener(27, handleEsc);
31527                 buttons = {};
31528                 var bt = this.buttonText;
31529                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31530                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31531                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31532                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31533                 bodyEl = dlg.body.createChild({
31534
31535                     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>'
31536                 });
31537                 msgEl = bodyEl.dom.firstChild;
31538                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31539                 textboxEl.enableDisplayMode();
31540                 textboxEl.addKeyListener([10,13], function(){
31541                     if(dlg.isVisible() && opt && opt.buttons){
31542                         if(opt.buttons.ok){
31543                             handleButton("ok");
31544                         }else if(opt.buttons.yes){
31545                             handleButton("yes");
31546                         }
31547                     }
31548                 });
31549                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31550                 textareaEl.enableDisplayMode();
31551                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31552                 progressEl.enableDisplayMode();
31553                 var pf = progressEl.dom.firstChild;
31554                 if (pf) {
31555                     pp = Roo.get(pf.firstChild);
31556                     pp.setHeight(pf.offsetHeight);
31557                 }
31558                 
31559             }
31560             return dlg;
31561         },
31562
31563         /**
31564          * Updates the message box body text
31565          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31566          * the XHTML-compliant non-breaking space character '&amp;#160;')
31567          * @return {Roo.MessageBox} This message box
31568          */
31569         updateText : function(text){
31570             if(!dlg.isVisible() && !opt.width){
31571                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31572             }
31573             msgEl.innerHTML = text || '&#160;';
31574       
31575             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31576             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31577             var w = Math.max(
31578                     Math.min(opt.width || cw , this.maxWidth), 
31579                     Math.max(opt.minWidth || this.minWidth, bwidth)
31580             );
31581             if(opt.prompt){
31582                 activeTextEl.setWidth(w);
31583             }
31584             if(dlg.isVisible()){
31585                 dlg.fixedcenter = false;
31586             }
31587             // to big, make it scroll. = But as usual stupid IE does not support
31588             // !important..
31589             
31590             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31591                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31592                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31593             } else {
31594                 bodyEl.dom.style.height = '';
31595                 bodyEl.dom.style.overflowY = '';
31596             }
31597             if (cw > w) {
31598                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31599             } else {
31600                 bodyEl.dom.style.overflowX = '';
31601             }
31602             
31603             dlg.setContentSize(w, bodyEl.getHeight());
31604             if(dlg.isVisible()){
31605                 dlg.fixedcenter = true;
31606             }
31607             return this;
31608         },
31609
31610         /**
31611          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31612          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31613          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31614          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31615          * @return {Roo.MessageBox} This message box
31616          */
31617         updateProgress : function(value, text){
31618             if(text){
31619                 this.updateText(text);
31620             }
31621             if (pp) { // weird bug on my firefox - for some reason this is not defined
31622                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31623             }
31624             return this;
31625         },        
31626
31627         /**
31628          * Returns true if the message box is currently displayed
31629          * @return {Boolean} True if the message box is visible, else false
31630          */
31631         isVisible : function(){
31632             return dlg && dlg.isVisible();  
31633         },
31634
31635         /**
31636          * Hides the message box if it is displayed
31637          */
31638         hide : function(){
31639             if(this.isVisible()){
31640                 dlg.hide();
31641             }  
31642         },
31643
31644         /**
31645          * Displays a new message box, or reinitializes an existing message box, based on the config options
31646          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31647          * The following config object properties are supported:
31648          * <pre>
31649 Property    Type             Description
31650 ----------  ---------------  ------------------------------------------------------------------------------------
31651 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31652                                    closes (defaults to undefined)
31653 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31654                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31655 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31656                                    progress and wait dialogs will ignore this property and always hide the
31657                                    close button as they can only be closed programmatically.
31658 cls               String           A custom CSS class to apply to the message box element
31659 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31660                                    displayed (defaults to 75)
31661 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31662                                    function will be btn (the name of the button that was clicked, if applicable,
31663                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31664                                    Progress and wait dialogs will ignore this option since they do not respond to
31665                                    user actions and can only be closed programmatically, so any required function
31666                                    should be called by the same code after it closes the dialog.
31667 icon              String           A CSS class that provides a background image to be used as an icon for
31668                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31669 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31670 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31671 modal             Boolean          False to allow user interaction with the page while the message box is
31672                                    displayed (defaults to true)
31673 msg               String           A string that will replace the existing message box body text (defaults
31674                                    to the XHTML-compliant non-breaking space character '&#160;')
31675 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31676 progress          Boolean          True to display a progress bar (defaults to false)
31677 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31678 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31679 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31680 title             String           The title text
31681 value             String           The string value to set into the active textbox element if displayed
31682 wait              Boolean          True to display a progress bar (defaults to false)
31683 width             Number           The width of the dialog in pixels
31684 </pre>
31685          *
31686          * Example usage:
31687          * <pre><code>
31688 Roo.Msg.show({
31689    title: 'Address',
31690    msg: 'Please enter your address:',
31691    width: 300,
31692    buttons: Roo.MessageBox.OKCANCEL,
31693    multiline: true,
31694    fn: saveAddress,
31695    animEl: 'addAddressBtn'
31696 });
31697 </code></pre>
31698          * @param {Object} config Configuration options
31699          * @return {Roo.MessageBox} This message box
31700          */
31701         show : function(options)
31702         {
31703             
31704             // this causes nightmares if you show one dialog after another
31705             // especially on callbacks..
31706              
31707             if(this.isVisible()){
31708                 
31709                 this.hide();
31710                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31711                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31712                 Roo.log("New Dialog Message:" +  options.msg )
31713                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31714                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31715                 
31716             }
31717             var d = this.getDialog();
31718             opt = options;
31719             d.setTitle(opt.title || "&#160;");
31720             d.close.setDisplayed(opt.closable !== false);
31721             activeTextEl = textboxEl;
31722             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31723             if(opt.prompt){
31724                 if(opt.multiline){
31725                     textboxEl.hide();
31726                     textareaEl.show();
31727                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31728                         opt.multiline : this.defaultTextHeight);
31729                     activeTextEl = textareaEl;
31730                 }else{
31731                     textboxEl.show();
31732                     textareaEl.hide();
31733                 }
31734             }else{
31735                 textboxEl.hide();
31736                 textareaEl.hide();
31737             }
31738             progressEl.setDisplayed(opt.progress === true);
31739             this.updateProgress(0);
31740             activeTextEl.dom.value = opt.value || "";
31741             if(opt.prompt){
31742                 dlg.setDefaultButton(activeTextEl);
31743             }else{
31744                 var bs = opt.buttons;
31745                 var db = null;
31746                 if(bs && bs.ok){
31747                     db = buttons["ok"];
31748                 }else if(bs && bs.yes){
31749                     db = buttons["yes"];
31750                 }
31751                 dlg.setDefaultButton(db);
31752             }
31753             bwidth = updateButtons(opt.buttons);
31754             this.updateText(opt.msg);
31755             if(opt.cls){
31756                 d.el.addClass(opt.cls);
31757             }
31758             d.proxyDrag = opt.proxyDrag === true;
31759             d.modal = opt.modal !== false;
31760             d.mask = opt.modal !== false ? mask : false;
31761             if(!d.isVisible()){
31762                 // force it to the end of the z-index stack so it gets a cursor in FF
31763                 document.body.appendChild(dlg.el.dom);
31764                 d.animateTarget = null;
31765                 d.show(options.animEl);
31766             }
31767             return this;
31768         },
31769
31770         /**
31771          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31772          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31773          * and closing the message box when the process is complete.
31774          * @param {String} title The title bar text
31775          * @param {String} msg The message box body text
31776          * @return {Roo.MessageBox} This message box
31777          */
31778         progress : function(title, msg){
31779             this.show({
31780                 title : title,
31781                 msg : msg,
31782                 buttons: false,
31783                 progress:true,
31784                 closable:false,
31785                 minWidth: this.minProgressWidth,
31786                 modal : true
31787             });
31788             return this;
31789         },
31790
31791         /**
31792          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31793          * If a callback function is passed it will be called after the user clicks the button, and the
31794          * id of the button that was clicked will be passed as the only parameter to the callback
31795          * (could also be the top-right close button).
31796          * @param {String} title The title bar text
31797          * @param {String} msg The message box body text
31798          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31799          * @param {Object} scope (optional) The scope of the callback function
31800          * @return {Roo.MessageBox} This message box
31801          */
31802         alert : function(title, msg, fn, scope){
31803             this.show({
31804                 title : title,
31805                 msg : msg,
31806                 buttons: this.OK,
31807                 fn: fn,
31808                 scope : scope,
31809                 modal : true
31810             });
31811             return this;
31812         },
31813
31814         /**
31815          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31816          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31817          * You are responsible for closing the message box when the process is complete.
31818          * @param {String} msg The message box body text
31819          * @param {String} title (optional) The title bar text
31820          * @return {Roo.MessageBox} This message box
31821          */
31822         wait : function(msg, title){
31823             this.show({
31824                 title : title,
31825                 msg : msg,
31826                 buttons: false,
31827                 closable:false,
31828                 progress:true,
31829                 modal:true,
31830                 width:300,
31831                 wait:true
31832             });
31833             waitTimer = Roo.TaskMgr.start({
31834                 run: function(i){
31835                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31836                 },
31837                 interval: 1000
31838             });
31839             return this;
31840         },
31841
31842         /**
31843          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31844          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31845          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31846          * @param {String} title The title bar text
31847          * @param {String} msg The message box body text
31848          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31849          * @param {Object} scope (optional) The scope of the callback function
31850          * @return {Roo.MessageBox} This message box
31851          */
31852         confirm : function(title, msg, fn, scope){
31853             this.show({
31854                 title : title,
31855                 msg : msg,
31856                 buttons: this.YESNO,
31857                 fn: fn,
31858                 scope : scope,
31859                 modal : true
31860             });
31861             return this;
31862         },
31863
31864         /**
31865          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31866          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31867          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31868          * (could also be the top-right close button) and the text that was entered will be passed as the two
31869          * parameters to the callback.
31870          * @param {String} title The title bar text
31871          * @param {String} msg The message box body text
31872          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31873          * @param {Object} scope (optional) The scope of the callback function
31874          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31875          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31876          * @return {Roo.MessageBox} This message box
31877          */
31878         prompt : function(title, msg, fn, scope, multiline){
31879             this.show({
31880                 title : title,
31881                 msg : msg,
31882                 buttons: this.OKCANCEL,
31883                 fn: fn,
31884                 minWidth:250,
31885                 scope : scope,
31886                 prompt:true,
31887                 multiline: multiline,
31888                 modal : true
31889             });
31890             return this;
31891         },
31892
31893         /**
31894          * Button config that displays a single OK button
31895          * @type Object
31896          */
31897         OK : {ok:true},
31898         /**
31899          * Button config that displays Yes and No buttons
31900          * @type Object
31901          */
31902         YESNO : {yes:true, no:true},
31903         /**
31904          * Button config that displays OK and Cancel buttons
31905          * @type Object
31906          */
31907         OKCANCEL : {ok:true, cancel:true},
31908         /**
31909          * Button config that displays Yes, No and Cancel buttons
31910          * @type Object
31911          */
31912         YESNOCANCEL : {yes:true, no:true, cancel:true},
31913
31914         /**
31915          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31916          * @type Number
31917          */
31918         defaultTextHeight : 75,
31919         /**
31920          * The maximum width in pixels of the message box (defaults to 600)
31921          * @type Number
31922          */
31923         maxWidth : 600,
31924         /**
31925          * The minimum width in pixels of the message box (defaults to 100)
31926          * @type Number
31927          */
31928         minWidth : 100,
31929         /**
31930          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31931          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31932          * @type Number
31933          */
31934         minProgressWidth : 250,
31935         /**
31936          * An object containing the default button text strings that can be overriden for localized language support.
31937          * Supported properties are: ok, cancel, yes and no.
31938          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31939          * @type Object
31940          */
31941         buttonText : {
31942             ok : "OK",
31943             cancel : "Cancel",
31944             yes : "Yes",
31945             no : "No"
31946         }
31947     };
31948 }();
31949
31950 /**
31951  * Shorthand for {@link Roo.MessageBox}
31952  */
31953 Roo.Msg = Roo.MessageBox;/*
31954  * Based on:
31955  * Ext JS Library 1.1.1
31956  * Copyright(c) 2006-2007, Ext JS, LLC.
31957  *
31958  * Originally Released Under LGPL - original licence link has changed is not relivant.
31959  *
31960  * Fork - LGPL
31961  * <script type="text/javascript">
31962  */
31963 /**
31964  * @class Roo.QuickTips
31965  * Provides attractive and customizable tooltips for any element.
31966  * @singleton
31967  */
31968 Roo.QuickTips = function(){
31969     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31970     var ce, bd, xy, dd;
31971     var visible = false, disabled = true, inited = false;
31972     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31973     
31974     var onOver = function(e){
31975         if(disabled){
31976             return;
31977         }
31978         var t = e.getTarget();
31979         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31980             return;
31981         }
31982         if(ce && t == ce.el){
31983             clearTimeout(hideProc);
31984             return;
31985         }
31986         if(t && tagEls[t.id]){
31987             tagEls[t.id].el = t;
31988             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31989             return;
31990         }
31991         var ttp, et = Roo.fly(t);
31992         var ns = cfg.namespace;
31993         if(tm.interceptTitles && t.title){
31994             ttp = t.title;
31995             t.qtip = ttp;
31996             t.removeAttribute("title");
31997             e.preventDefault();
31998         }else{
31999             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
32000         }
32001         if(ttp){
32002             showProc = show.defer(tm.showDelay, tm, [{
32003                 el: t, 
32004                 text: ttp, 
32005                 width: et.getAttributeNS(ns, cfg.width),
32006                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32007                 title: et.getAttributeNS(ns, cfg.title),
32008                     cls: et.getAttributeNS(ns, cfg.cls)
32009             }]);
32010         }
32011     };
32012     
32013     var onOut = function(e){
32014         clearTimeout(showProc);
32015         var t = e.getTarget();
32016         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32017             hideProc = setTimeout(hide, tm.hideDelay);
32018         }
32019     };
32020     
32021     var onMove = function(e){
32022         if(disabled){
32023             return;
32024         }
32025         xy = e.getXY();
32026         xy[1] += 18;
32027         if(tm.trackMouse && ce){
32028             el.setXY(xy);
32029         }
32030     };
32031     
32032     var onDown = function(e){
32033         clearTimeout(showProc);
32034         clearTimeout(hideProc);
32035         if(!e.within(el)){
32036             if(tm.hideOnClick){
32037                 hide();
32038                 tm.disable();
32039                 tm.enable.defer(100, tm);
32040             }
32041         }
32042     };
32043     
32044     var getPad = function(){
32045         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32046     };
32047
32048     var show = function(o){
32049         if(disabled){
32050             return;
32051         }
32052         clearTimeout(dismissProc);
32053         ce = o;
32054         if(removeCls){ // in case manually hidden
32055             el.removeClass(removeCls);
32056             removeCls = null;
32057         }
32058         if(ce.cls){
32059             el.addClass(ce.cls);
32060             removeCls = ce.cls;
32061         }
32062         if(ce.title){
32063             tipTitle.update(ce.title);
32064             tipTitle.show();
32065         }else{
32066             tipTitle.update('');
32067             tipTitle.hide();
32068         }
32069         el.dom.style.width  = tm.maxWidth+'px';
32070         //tipBody.dom.style.width = '';
32071         tipBodyText.update(o.text);
32072         var p = getPad(), w = ce.width;
32073         if(!w){
32074             var td = tipBodyText.dom;
32075             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32076             if(aw > tm.maxWidth){
32077                 w = tm.maxWidth;
32078             }else if(aw < tm.minWidth){
32079                 w = tm.minWidth;
32080             }else{
32081                 w = aw;
32082             }
32083         }
32084         //tipBody.setWidth(w);
32085         el.setWidth(parseInt(w, 10) + p);
32086         if(ce.autoHide === false){
32087             close.setDisplayed(true);
32088             if(dd){
32089                 dd.unlock();
32090             }
32091         }else{
32092             close.setDisplayed(false);
32093             if(dd){
32094                 dd.lock();
32095             }
32096         }
32097         if(xy){
32098             el.avoidY = xy[1]-18;
32099             el.setXY(xy);
32100         }
32101         if(tm.animate){
32102             el.setOpacity(.1);
32103             el.setStyle("visibility", "visible");
32104             el.fadeIn({callback: afterShow});
32105         }else{
32106             afterShow();
32107         }
32108     };
32109     
32110     var afterShow = function(){
32111         if(ce){
32112             el.show();
32113             esc.enable();
32114             if(tm.autoDismiss && ce.autoHide !== false){
32115                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32116             }
32117         }
32118     };
32119     
32120     var hide = function(noanim){
32121         clearTimeout(dismissProc);
32122         clearTimeout(hideProc);
32123         ce = null;
32124         if(el.isVisible()){
32125             esc.disable();
32126             if(noanim !== true && tm.animate){
32127                 el.fadeOut({callback: afterHide});
32128             }else{
32129                 afterHide();
32130             } 
32131         }
32132     };
32133     
32134     var afterHide = function(){
32135         el.hide();
32136         if(removeCls){
32137             el.removeClass(removeCls);
32138             removeCls = null;
32139         }
32140     };
32141     
32142     return {
32143         /**
32144         * @cfg {Number} minWidth
32145         * The minimum width of the quick tip (defaults to 40)
32146         */
32147        minWidth : 40,
32148         /**
32149         * @cfg {Number} maxWidth
32150         * The maximum width of the quick tip (defaults to 300)
32151         */
32152        maxWidth : 300,
32153         /**
32154         * @cfg {Boolean} interceptTitles
32155         * True to automatically use the element's DOM title value if available (defaults to false)
32156         */
32157        interceptTitles : false,
32158         /**
32159         * @cfg {Boolean} trackMouse
32160         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32161         */
32162        trackMouse : false,
32163         /**
32164         * @cfg {Boolean} hideOnClick
32165         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32166         */
32167        hideOnClick : true,
32168         /**
32169         * @cfg {Number} showDelay
32170         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32171         */
32172        showDelay : 500,
32173         /**
32174         * @cfg {Number} hideDelay
32175         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32176         */
32177        hideDelay : 200,
32178         /**
32179         * @cfg {Boolean} autoHide
32180         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32181         * Used in conjunction with hideDelay.
32182         */
32183        autoHide : true,
32184         /**
32185         * @cfg {Boolean}
32186         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32187         * (defaults to true).  Used in conjunction with autoDismissDelay.
32188         */
32189        autoDismiss : true,
32190         /**
32191         * @cfg {Number}
32192         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32193         */
32194        autoDismissDelay : 5000,
32195        /**
32196         * @cfg {Boolean} animate
32197         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32198         */
32199        animate : false,
32200
32201        /**
32202         * @cfg {String} title
32203         * Title text to display (defaults to '').  This can be any valid HTML markup.
32204         */
32205         title: '',
32206        /**
32207         * @cfg {String} text
32208         * Body text to display (defaults to '').  This can be any valid HTML markup.
32209         */
32210         text : '',
32211        /**
32212         * @cfg {String} cls
32213         * A CSS class to apply to the base quick tip element (defaults to '').
32214         */
32215         cls : '',
32216        /**
32217         * @cfg {Number} width
32218         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32219         * minWidth or maxWidth.
32220         */
32221         width : null,
32222
32223     /**
32224      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32225      * or display QuickTips in a page.
32226      */
32227        init : function(){
32228           tm = Roo.QuickTips;
32229           cfg = tm.tagConfig;
32230           if(!inited){
32231               if(!Roo.isReady){ // allow calling of init() before onReady
32232                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32233                   return;
32234               }
32235               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32236               el.fxDefaults = {stopFx: true};
32237               // maximum custom styling
32238               //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>');
32239               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>');              
32240               tipTitle = el.child('h3');
32241               tipTitle.enableDisplayMode("block");
32242               tipBody = el.child('div.x-tip-bd');
32243               tipBodyText = el.child('div.x-tip-bd-inner');
32244               //bdLeft = el.child('div.x-tip-bd-left');
32245               //bdRight = el.child('div.x-tip-bd-right');
32246               close = el.child('div.x-tip-close');
32247               close.enableDisplayMode("block");
32248               close.on("click", hide);
32249               var d = Roo.get(document);
32250               d.on("mousedown", onDown);
32251               d.on("mouseover", onOver);
32252               d.on("mouseout", onOut);
32253               d.on("mousemove", onMove);
32254               esc = d.addKeyListener(27, hide);
32255               esc.disable();
32256               if(Roo.dd.DD){
32257                   dd = el.initDD("default", null, {
32258                       onDrag : function(){
32259                           el.sync();  
32260                       }
32261                   });
32262                   dd.setHandleElId(tipTitle.id);
32263                   dd.lock();
32264               }
32265               inited = true;
32266           }
32267           this.enable(); 
32268        },
32269
32270     /**
32271      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32272      * are supported:
32273      * <pre>
32274 Property    Type                   Description
32275 ----------  ---------------------  ------------------------------------------------------------------------
32276 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32277      * </ul>
32278      * @param {Object} config The config object
32279      */
32280        register : function(config){
32281            var cs = config instanceof Array ? config : arguments;
32282            for(var i = 0, len = cs.length; i < len; i++) {
32283                var c = cs[i];
32284                var target = c.target;
32285                if(target){
32286                    if(target instanceof Array){
32287                        for(var j = 0, jlen = target.length; j < jlen; j++){
32288                            tagEls[target[j]] = c;
32289                        }
32290                    }else{
32291                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32292                    }
32293                }
32294            }
32295        },
32296
32297     /**
32298      * Removes this quick tip from its element and destroys it.
32299      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32300      */
32301        unregister : function(el){
32302            delete tagEls[Roo.id(el)];
32303        },
32304
32305     /**
32306      * Enable this quick tip.
32307      */
32308        enable : function(){
32309            if(inited && disabled){
32310                locks.pop();
32311                if(locks.length < 1){
32312                    disabled = false;
32313                }
32314            }
32315        },
32316
32317     /**
32318      * Disable this quick tip.
32319      */
32320        disable : function(){
32321           disabled = true;
32322           clearTimeout(showProc);
32323           clearTimeout(hideProc);
32324           clearTimeout(dismissProc);
32325           if(ce){
32326               hide(true);
32327           }
32328           locks.push(1);
32329        },
32330
32331     /**
32332      * Returns true if the quick tip is enabled, else false.
32333      */
32334        isEnabled : function(){
32335             return !disabled;
32336        },
32337
32338         // private
32339        tagConfig : {
32340            namespace : "ext",
32341            attribute : "qtip",
32342            width : "width",
32343            target : "target",
32344            title : "qtitle",
32345            hide : "hide",
32346            cls : "qclass"
32347        }
32348    };
32349 }();
32350
32351 // backwards compat
32352 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32353  * Based on:
32354  * Ext JS Library 1.1.1
32355  * Copyright(c) 2006-2007, Ext JS, LLC.
32356  *
32357  * Originally Released Under LGPL - original licence link has changed is not relivant.
32358  *
32359  * Fork - LGPL
32360  * <script type="text/javascript">
32361  */
32362  
32363
32364 /**
32365  * @class Roo.tree.TreePanel
32366  * @extends Roo.data.Tree
32367
32368  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32369  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32370  * @cfg {Boolean} enableDD true to enable drag and drop
32371  * @cfg {Boolean} enableDrag true to enable just drag
32372  * @cfg {Boolean} enableDrop true to enable just drop
32373  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32374  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32375  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32376  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32377  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32378  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32379  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32380  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32381  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32382  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32383  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32384  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32385  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32386  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32387  * @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>
32388  * @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>
32389  * 
32390  * @constructor
32391  * @param {String/HTMLElement/Element} el The container element
32392  * @param {Object} config
32393  */
32394 Roo.tree.TreePanel = function(el, config){
32395     var root = false;
32396     var loader = false;
32397     if (config.root) {
32398         root = config.root;
32399         delete config.root;
32400     }
32401     if (config.loader) {
32402         loader = config.loader;
32403         delete config.loader;
32404     }
32405     
32406     Roo.apply(this, config);
32407     Roo.tree.TreePanel.superclass.constructor.call(this);
32408     this.el = Roo.get(el);
32409     this.el.addClass('x-tree');
32410     //console.log(root);
32411     if (root) {
32412         this.setRootNode( Roo.factory(root, Roo.tree));
32413     }
32414     if (loader) {
32415         this.loader = Roo.factory(loader, Roo.tree);
32416     }
32417    /**
32418     * Read-only. The id of the container element becomes this TreePanel's id.
32419     */
32420     this.id = this.el.id;
32421     this.addEvents({
32422         /**
32423         * @event beforeload
32424         * Fires before a node is loaded, return false to cancel
32425         * @param {Node} node The node being loaded
32426         */
32427         "beforeload" : true,
32428         /**
32429         * @event load
32430         * Fires when a node is loaded
32431         * @param {Node} node The node that was loaded
32432         */
32433         "load" : true,
32434         /**
32435         * @event textchange
32436         * Fires when the text for a node is changed
32437         * @param {Node} node The node
32438         * @param {String} text The new text
32439         * @param {String} oldText The old text
32440         */
32441         "textchange" : true,
32442         /**
32443         * @event beforeexpand
32444         * Fires before a node is expanded, return false to cancel.
32445         * @param {Node} node The node
32446         * @param {Boolean} deep
32447         * @param {Boolean} anim
32448         */
32449         "beforeexpand" : true,
32450         /**
32451         * @event beforecollapse
32452         * Fires before a node is collapsed, return false to cancel.
32453         * @param {Node} node The node
32454         * @param {Boolean} deep
32455         * @param {Boolean} anim
32456         */
32457         "beforecollapse" : true,
32458         /**
32459         * @event expand
32460         * Fires when a node is expanded
32461         * @param {Node} node The node
32462         */
32463         "expand" : true,
32464         /**
32465         * @event disabledchange
32466         * Fires when the disabled status of a node changes
32467         * @param {Node} node The node
32468         * @param {Boolean} disabled
32469         */
32470         "disabledchange" : true,
32471         /**
32472         * @event collapse
32473         * Fires when a node is collapsed
32474         * @param {Node} node The node
32475         */
32476         "collapse" : true,
32477         /**
32478         * @event beforeclick
32479         * Fires before click processing on a node. Return false to cancel the default action.
32480         * @param {Node} node The node
32481         * @param {Roo.EventObject} e The event object
32482         */
32483         "beforeclick":true,
32484         /**
32485         * @event checkchange
32486         * Fires when a node with a checkbox's checked property changes
32487         * @param {Node} this This node
32488         * @param {Boolean} checked
32489         */
32490         "checkchange":true,
32491         /**
32492         * @event click
32493         * Fires when a node is clicked
32494         * @param {Node} node The node
32495         * @param {Roo.EventObject} e The event object
32496         */
32497         "click":true,
32498         /**
32499         * @event dblclick
32500         * Fires when a node is double clicked
32501         * @param {Node} node The node
32502         * @param {Roo.EventObject} e The event object
32503         */
32504         "dblclick":true,
32505         /**
32506         * @event contextmenu
32507         * Fires when a node is right clicked
32508         * @param {Node} node The node
32509         * @param {Roo.EventObject} e The event object
32510         */
32511         "contextmenu":true,
32512         /**
32513         * @event beforechildrenrendered
32514         * Fires right before the child nodes for a node are rendered
32515         * @param {Node} node The node
32516         */
32517         "beforechildrenrendered":true,
32518         /**
32519         * @event startdrag
32520         * Fires when a node starts being dragged
32521         * @param {Roo.tree.TreePanel} this
32522         * @param {Roo.tree.TreeNode} node
32523         * @param {event} e The raw browser event
32524         */ 
32525        "startdrag" : true,
32526        /**
32527         * @event enddrag
32528         * Fires when a drag operation is complete
32529         * @param {Roo.tree.TreePanel} this
32530         * @param {Roo.tree.TreeNode} node
32531         * @param {event} e The raw browser event
32532         */
32533        "enddrag" : true,
32534        /**
32535         * @event dragdrop
32536         * Fires when a dragged node is dropped on a valid DD target
32537         * @param {Roo.tree.TreePanel} this
32538         * @param {Roo.tree.TreeNode} node
32539         * @param {DD} dd The dd it was dropped on
32540         * @param {event} e The raw browser event
32541         */
32542        "dragdrop" : true,
32543        /**
32544         * @event beforenodedrop
32545         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32546         * passed to handlers has the following properties:<br />
32547         * <ul style="padding:5px;padding-left:16px;">
32548         * <li>tree - The TreePanel</li>
32549         * <li>target - The node being targeted for the drop</li>
32550         * <li>data - The drag data from the drag source</li>
32551         * <li>point - The point of the drop - append, above or below</li>
32552         * <li>source - The drag source</li>
32553         * <li>rawEvent - Raw mouse event</li>
32554         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32555         * to be inserted by setting them on this object.</li>
32556         * <li>cancel - Set this to true to cancel the drop.</li>
32557         * </ul>
32558         * @param {Object} dropEvent
32559         */
32560        "beforenodedrop" : true,
32561        /**
32562         * @event nodedrop
32563         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32564         * passed to handlers has the following properties:<br />
32565         * <ul style="padding:5px;padding-left:16px;">
32566         * <li>tree - The TreePanel</li>
32567         * <li>target - The node being targeted for the drop</li>
32568         * <li>data - The drag data from the drag source</li>
32569         * <li>point - The point of the drop - append, above or below</li>
32570         * <li>source - The drag source</li>
32571         * <li>rawEvent - Raw mouse event</li>
32572         * <li>dropNode - Dropped node(s).</li>
32573         * </ul>
32574         * @param {Object} dropEvent
32575         */
32576        "nodedrop" : true,
32577         /**
32578         * @event nodedragover
32579         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32580         * passed to handlers has the following properties:<br />
32581         * <ul style="padding:5px;padding-left:16px;">
32582         * <li>tree - The TreePanel</li>
32583         * <li>target - The node being targeted for the drop</li>
32584         * <li>data - The drag data from the drag source</li>
32585         * <li>point - The point of the drop - append, above or below</li>
32586         * <li>source - The drag source</li>
32587         * <li>rawEvent - Raw mouse event</li>
32588         * <li>dropNode - Drop node(s) provided by the source.</li>
32589         * <li>cancel - Set this to true to signal drop not allowed.</li>
32590         * </ul>
32591         * @param {Object} dragOverEvent
32592         */
32593        "nodedragover" : true
32594         
32595     });
32596     if(this.singleExpand){
32597        this.on("beforeexpand", this.restrictExpand, this);
32598     }
32599     if (this.editor) {
32600         this.editor.tree = this;
32601         this.editor = Roo.factory(this.editor, Roo.tree);
32602     }
32603     
32604     if (this.selModel) {
32605         this.selModel = Roo.factory(this.selModel, Roo.tree);
32606     }
32607    
32608 };
32609 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32610     rootVisible : true,
32611     animate: Roo.enableFx,
32612     lines : true,
32613     enableDD : false,
32614     hlDrop : Roo.enableFx,
32615   
32616     renderer: false,
32617     
32618     rendererTip: false,
32619     // private
32620     restrictExpand : function(node){
32621         var p = node.parentNode;
32622         if(p){
32623             if(p.expandedChild && p.expandedChild.parentNode == p){
32624                 p.expandedChild.collapse();
32625             }
32626             p.expandedChild = node;
32627         }
32628     },
32629
32630     // private override
32631     setRootNode : function(node){
32632         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32633         if(!this.rootVisible){
32634             node.ui = new Roo.tree.RootTreeNodeUI(node);
32635         }
32636         return node;
32637     },
32638
32639     /**
32640      * Returns the container element for this TreePanel
32641      */
32642     getEl : function(){
32643         return this.el;
32644     },
32645
32646     /**
32647      * Returns the default TreeLoader for this TreePanel
32648      */
32649     getLoader : function(){
32650         return this.loader;
32651     },
32652
32653     /**
32654      * Expand all nodes
32655      */
32656     expandAll : function(){
32657         this.root.expand(true);
32658     },
32659
32660     /**
32661      * Collapse all nodes
32662      */
32663     collapseAll : function(){
32664         this.root.collapse(true);
32665     },
32666
32667     /**
32668      * Returns the selection model used by this TreePanel
32669      */
32670     getSelectionModel : function(){
32671         if(!this.selModel){
32672             this.selModel = new Roo.tree.DefaultSelectionModel();
32673         }
32674         return this.selModel;
32675     },
32676
32677     /**
32678      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32679      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32680      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32681      * @return {Array}
32682      */
32683     getChecked : function(a, startNode){
32684         startNode = startNode || this.root;
32685         var r = [];
32686         var f = function(){
32687             if(this.attributes.checked){
32688                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32689             }
32690         }
32691         startNode.cascade(f);
32692         return r;
32693     },
32694
32695     /**
32696      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32697      * @param {String} path
32698      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32699      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32700      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32701      */
32702     expandPath : function(path, attr, callback){
32703         attr = attr || "id";
32704         var keys = path.split(this.pathSeparator);
32705         var curNode = this.root;
32706         if(curNode.attributes[attr] != keys[1]){ // invalid root
32707             if(callback){
32708                 callback(false, null);
32709             }
32710             return;
32711         }
32712         var index = 1;
32713         var f = function(){
32714             if(++index == keys.length){
32715                 if(callback){
32716                     callback(true, curNode);
32717                 }
32718                 return;
32719             }
32720             var c = curNode.findChild(attr, keys[index]);
32721             if(!c){
32722                 if(callback){
32723                     callback(false, curNode);
32724                 }
32725                 return;
32726             }
32727             curNode = c;
32728             c.expand(false, false, f);
32729         };
32730         curNode.expand(false, false, f);
32731     },
32732
32733     /**
32734      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32735      * @param {String} path
32736      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32737      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32738      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32739      */
32740     selectPath : function(path, attr, callback){
32741         attr = attr || "id";
32742         var keys = path.split(this.pathSeparator);
32743         var v = keys.pop();
32744         if(keys.length > 0){
32745             var f = function(success, node){
32746                 if(success && node){
32747                     var n = node.findChild(attr, v);
32748                     if(n){
32749                         n.select();
32750                         if(callback){
32751                             callback(true, n);
32752                         }
32753                     }else if(callback){
32754                         callback(false, n);
32755                     }
32756                 }else{
32757                     if(callback){
32758                         callback(false, n);
32759                     }
32760                 }
32761             };
32762             this.expandPath(keys.join(this.pathSeparator), attr, f);
32763         }else{
32764             this.root.select();
32765             if(callback){
32766                 callback(true, this.root);
32767             }
32768         }
32769     },
32770
32771     getTreeEl : function(){
32772         return this.el;
32773     },
32774
32775     /**
32776      * Trigger rendering of this TreePanel
32777      */
32778     render : function(){
32779         if (this.innerCt) {
32780             return this; // stop it rendering more than once!!
32781         }
32782         
32783         this.innerCt = this.el.createChild({tag:"ul",
32784                cls:"x-tree-root-ct " +
32785                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32786
32787         if(this.containerScroll){
32788             Roo.dd.ScrollManager.register(this.el);
32789         }
32790         if((this.enableDD || this.enableDrop) && !this.dropZone){
32791            /**
32792             * The dropZone used by this tree if drop is enabled
32793             * @type Roo.tree.TreeDropZone
32794             */
32795              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32796                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32797            });
32798         }
32799         if((this.enableDD || this.enableDrag) && !this.dragZone){
32800            /**
32801             * The dragZone used by this tree if drag is enabled
32802             * @type Roo.tree.TreeDragZone
32803             */
32804             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32805                ddGroup: this.ddGroup || "TreeDD",
32806                scroll: this.ddScroll
32807            });
32808         }
32809         this.getSelectionModel().init(this);
32810         if (!this.root) {
32811             Roo.log("ROOT not set in tree");
32812             return this;
32813         }
32814         this.root.render();
32815         if(!this.rootVisible){
32816             this.root.renderChildren();
32817         }
32818         return this;
32819     }
32820 });/*
32821  * Based on:
32822  * Ext JS Library 1.1.1
32823  * Copyright(c) 2006-2007, Ext JS, LLC.
32824  *
32825  * Originally Released Under LGPL - original licence link has changed is not relivant.
32826  *
32827  * Fork - LGPL
32828  * <script type="text/javascript">
32829  */
32830  
32831
32832 /**
32833  * @class Roo.tree.DefaultSelectionModel
32834  * @extends Roo.util.Observable
32835  * The default single selection for a TreePanel.
32836  * @param {Object} cfg Configuration
32837  */
32838 Roo.tree.DefaultSelectionModel = function(cfg){
32839    this.selNode = null;
32840    
32841    
32842    
32843    this.addEvents({
32844        /**
32845         * @event selectionchange
32846         * Fires when the selected node changes
32847         * @param {DefaultSelectionModel} this
32848         * @param {TreeNode} node the new selection
32849         */
32850        "selectionchange" : true,
32851
32852        /**
32853         * @event beforeselect
32854         * Fires before the selected node changes, return false to cancel the change
32855         * @param {DefaultSelectionModel} this
32856         * @param {TreeNode} node the new selection
32857         * @param {TreeNode} node the old selection
32858         */
32859        "beforeselect" : true
32860    });
32861    
32862     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32863 };
32864
32865 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32866     init : function(tree){
32867         this.tree = tree;
32868         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32869         tree.on("click", this.onNodeClick, this);
32870     },
32871     
32872     onNodeClick : function(node, e){
32873         if (e.ctrlKey && this.selNode == node)  {
32874             this.unselect(node);
32875             return;
32876         }
32877         this.select(node);
32878     },
32879     
32880     /**
32881      * Select a node.
32882      * @param {TreeNode} node The node to select
32883      * @return {TreeNode} The selected node
32884      */
32885     select : function(node){
32886         var last = this.selNode;
32887         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32888             if(last){
32889                 last.ui.onSelectedChange(false);
32890             }
32891             this.selNode = node;
32892             node.ui.onSelectedChange(true);
32893             this.fireEvent("selectionchange", this, node, last);
32894         }
32895         return node;
32896     },
32897     
32898     /**
32899      * Deselect a node.
32900      * @param {TreeNode} node The node to unselect
32901      */
32902     unselect : function(node){
32903         if(this.selNode == node){
32904             this.clearSelections();
32905         }    
32906     },
32907     
32908     /**
32909      * Clear all selections
32910      */
32911     clearSelections : function(){
32912         var n = this.selNode;
32913         if(n){
32914             n.ui.onSelectedChange(false);
32915             this.selNode = null;
32916             this.fireEvent("selectionchange", this, null);
32917         }
32918         return n;
32919     },
32920     
32921     /**
32922      * Get the selected node
32923      * @return {TreeNode} The selected node
32924      */
32925     getSelectedNode : function(){
32926         return this.selNode;    
32927     },
32928     
32929     /**
32930      * Returns true if the node is selected
32931      * @param {TreeNode} node The node to check
32932      * @return {Boolean}
32933      */
32934     isSelected : function(node){
32935         return this.selNode == node;  
32936     },
32937
32938     /**
32939      * Selects the node above the selected node in the tree, intelligently walking the nodes
32940      * @return TreeNode The new selection
32941      */
32942     selectPrevious : function(){
32943         var s = this.selNode || this.lastSelNode;
32944         if(!s){
32945             return null;
32946         }
32947         var ps = s.previousSibling;
32948         if(ps){
32949             if(!ps.isExpanded() || ps.childNodes.length < 1){
32950                 return this.select(ps);
32951             } else{
32952                 var lc = ps.lastChild;
32953                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32954                     lc = lc.lastChild;
32955                 }
32956                 return this.select(lc);
32957             }
32958         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32959             return this.select(s.parentNode);
32960         }
32961         return null;
32962     },
32963
32964     /**
32965      * Selects the node above the selected node in the tree, intelligently walking the nodes
32966      * @return TreeNode The new selection
32967      */
32968     selectNext : function(){
32969         var s = this.selNode || this.lastSelNode;
32970         if(!s){
32971             return null;
32972         }
32973         if(s.firstChild && s.isExpanded()){
32974              return this.select(s.firstChild);
32975          }else if(s.nextSibling){
32976              return this.select(s.nextSibling);
32977          }else if(s.parentNode){
32978             var newS = null;
32979             s.parentNode.bubble(function(){
32980                 if(this.nextSibling){
32981                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32982                     return false;
32983                 }
32984             });
32985             return newS;
32986          }
32987         return null;
32988     },
32989
32990     onKeyDown : function(e){
32991         var s = this.selNode || this.lastSelNode;
32992         // undesirable, but required
32993         var sm = this;
32994         if(!s){
32995             return;
32996         }
32997         var k = e.getKey();
32998         switch(k){
32999              case e.DOWN:
33000                  e.stopEvent();
33001                  this.selectNext();
33002              break;
33003              case e.UP:
33004                  e.stopEvent();
33005                  this.selectPrevious();
33006              break;
33007              case e.RIGHT:
33008                  e.preventDefault();
33009                  if(s.hasChildNodes()){
33010                      if(!s.isExpanded()){
33011                          s.expand();
33012                      }else if(s.firstChild){
33013                          this.select(s.firstChild, e);
33014                      }
33015                  }
33016              break;
33017              case e.LEFT:
33018                  e.preventDefault();
33019                  if(s.hasChildNodes() && s.isExpanded()){
33020                      s.collapse();
33021                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33022                      this.select(s.parentNode, e);
33023                  }
33024              break;
33025         };
33026     }
33027 });
33028
33029 /**
33030  * @class Roo.tree.MultiSelectionModel
33031  * @extends Roo.util.Observable
33032  * Multi selection for a TreePanel.
33033  * @param {Object} cfg Configuration
33034  */
33035 Roo.tree.MultiSelectionModel = function(){
33036    this.selNodes = [];
33037    this.selMap = {};
33038    this.addEvents({
33039        /**
33040         * @event selectionchange
33041         * Fires when the selected nodes change
33042         * @param {MultiSelectionModel} this
33043         * @param {Array} nodes Array of the selected nodes
33044         */
33045        "selectionchange" : true
33046    });
33047    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33048    
33049 };
33050
33051 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33052     init : function(tree){
33053         this.tree = tree;
33054         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33055         tree.on("click", this.onNodeClick, this);
33056     },
33057     
33058     onNodeClick : function(node, e){
33059         this.select(node, e, e.ctrlKey);
33060     },
33061     
33062     /**
33063      * Select a node.
33064      * @param {TreeNode} node The node to select
33065      * @param {EventObject} e (optional) An event associated with the selection
33066      * @param {Boolean} keepExisting True to retain existing selections
33067      * @return {TreeNode} The selected node
33068      */
33069     select : function(node, e, keepExisting){
33070         if(keepExisting !== true){
33071             this.clearSelections(true);
33072         }
33073         if(this.isSelected(node)){
33074             this.lastSelNode = node;
33075             return node;
33076         }
33077         this.selNodes.push(node);
33078         this.selMap[node.id] = node;
33079         this.lastSelNode = node;
33080         node.ui.onSelectedChange(true);
33081         this.fireEvent("selectionchange", this, this.selNodes);
33082         return node;
33083     },
33084     
33085     /**
33086      * Deselect a node.
33087      * @param {TreeNode} node The node to unselect
33088      */
33089     unselect : function(node){
33090         if(this.selMap[node.id]){
33091             node.ui.onSelectedChange(false);
33092             var sn = this.selNodes;
33093             var index = -1;
33094             if(sn.indexOf){
33095                 index = sn.indexOf(node);
33096             }else{
33097                 for(var i = 0, len = sn.length; i < len; i++){
33098                     if(sn[i] == node){
33099                         index = i;
33100                         break;
33101                     }
33102                 }
33103             }
33104             if(index != -1){
33105                 this.selNodes.splice(index, 1);
33106             }
33107             delete this.selMap[node.id];
33108             this.fireEvent("selectionchange", this, this.selNodes);
33109         }
33110     },
33111     
33112     /**
33113      * Clear all selections
33114      */
33115     clearSelections : function(suppressEvent){
33116         var sn = this.selNodes;
33117         if(sn.length > 0){
33118             for(var i = 0, len = sn.length; i < len; i++){
33119                 sn[i].ui.onSelectedChange(false);
33120             }
33121             this.selNodes = [];
33122             this.selMap = {};
33123             if(suppressEvent !== true){
33124                 this.fireEvent("selectionchange", this, this.selNodes);
33125             }
33126         }
33127     },
33128     
33129     /**
33130      * Returns true if the node is selected
33131      * @param {TreeNode} node The node to check
33132      * @return {Boolean}
33133      */
33134     isSelected : function(node){
33135         return this.selMap[node.id] ? true : false;  
33136     },
33137     
33138     /**
33139      * Returns an array of the selected nodes
33140      * @return {Array}
33141      */
33142     getSelectedNodes : function(){
33143         return this.selNodes;    
33144     },
33145
33146     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33147
33148     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33149
33150     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33151 });/*
33152  * Based on:
33153  * Ext JS Library 1.1.1
33154  * Copyright(c) 2006-2007, Ext JS, LLC.
33155  *
33156  * Originally Released Under LGPL - original licence link has changed is not relivant.
33157  *
33158  * Fork - LGPL
33159  * <script type="text/javascript">
33160  */
33161  
33162 /**
33163  * @class Roo.tree.TreeNode
33164  * @extends Roo.data.Node
33165  * @cfg {String} text The text for this node
33166  * @cfg {Boolean} expanded true to start the node expanded
33167  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33168  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33169  * @cfg {Boolean} disabled true to start the node disabled
33170  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33171  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33172  * @cfg {String} cls A css class to be added to the node
33173  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33174  * @cfg {String} href URL of the link used for the node (defaults to #)
33175  * @cfg {String} hrefTarget target frame for the link
33176  * @cfg {String} qtip An Ext QuickTip for the node
33177  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33178  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33179  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33180  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33181  * (defaults to undefined with no checkbox rendered)
33182  * @constructor
33183  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33184  */
33185 Roo.tree.TreeNode = function(attributes){
33186     attributes = attributes || {};
33187     if(typeof attributes == "string"){
33188         attributes = {text: attributes};
33189     }
33190     this.childrenRendered = false;
33191     this.rendered = false;
33192     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33193     this.expanded = attributes.expanded === true;
33194     this.isTarget = attributes.isTarget !== false;
33195     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33196     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33197
33198     /**
33199      * Read-only. The text for this node. To change it use setText().
33200      * @type String
33201      */
33202     this.text = attributes.text;
33203     /**
33204      * True if this node is disabled.
33205      * @type Boolean
33206      */
33207     this.disabled = attributes.disabled === true;
33208
33209     this.addEvents({
33210         /**
33211         * @event textchange
33212         * Fires when the text for this node is changed
33213         * @param {Node} this This node
33214         * @param {String} text The new text
33215         * @param {String} oldText The old text
33216         */
33217         "textchange" : true,
33218         /**
33219         * @event beforeexpand
33220         * Fires before this node is expanded, return false to cancel.
33221         * @param {Node} this This node
33222         * @param {Boolean} deep
33223         * @param {Boolean} anim
33224         */
33225         "beforeexpand" : true,
33226         /**
33227         * @event beforecollapse
33228         * Fires before this node is collapsed, return false to cancel.
33229         * @param {Node} this This node
33230         * @param {Boolean} deep
33231         * @param {Boolean} anim
33232         */
33233         "beforecollapse" : true,
33234         /**
33235         * @event expand
33236         * Fires when this node is expanded
33237         * @param {Node} this This node
33238         */
33239         "expand" : true,
33240         /**
33241         * @event disabledchange
33242         * Fires when the disabled status of this node changes
33243         * @param {Node} this This node
33244         * @param {Boolean} disabled
33245         */
33246         "disabledchange" : true,
33247         /**
33248         * @event collapse
33249         * Fires when this node is collapsed
33250         * @param {Node} this This node
33251         */
33252         "collapse" : true,
33253         /**
33254         * @event beforeclick
33255         * Fires before click processing. Return false to cancel the default action.
33256         * @param {Node} this This node
33257         * @param {Roo.EventObject} e The event object
33258         */
33259         "beforeclick":true,
33260         /**
33261         * @event checkchange
33262         * Fires when a node with a checkbox's checked property changes
33263         * @param {Node} this This node
33264         * @param {Boolean} checked
33265         */
33266         "checkchange":true,
33267         /**
33268         * @event click
33269         * Fires when this node is clicked
33270         * @param {Node} this This node
33271         * @param {Roo.EventObject} e The event object
33272         */
33273         "click":true,
33274         /**
33275         * @event dblclick
33276         * Fires when this node is double clicked
33277         * @param {Node} this This node
33278         * @param {Roo.EventObject} e The event object
33279         */
33280         "dblclick":true,
33281         /**
33282         * @event contextmenu
33283         * Fires when this node is right clicked
33284         * @param {Node} this This node
33285         * @param {Roo.EventObject} e The event object
33286         */
33287         "contextmenu":true,
33288         /**
33289         * @event beforechildrenrendered
33290         * Fires right before the child nodes for this node are rendered
33291         * @param {Node} this This node
33292         */
33293         "beforechildrenrendered":true
33294     });
33295
33296     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33297
33298     /**
33299      * Read-only. The UI for this node
33300      * @type TreeNodeUI
33301      */
33302     this.ui = new uiClass(this);
33303     
33304     // finally support items[]
33305     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33306         return;
33307     }
33308     
33309     
33310     Roo.each(this.attributes.items, function(c) {
33311         this.appendChild(Roo.factory(c,Roo.Tree));
33312     }, this);
33313     delete this.attributes.items;
33314     
33315     
33316     
33317 };
33318 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33319     preventHScroll: true,
33320     /**
33321      * Returns true if this node is expanded
33322      * @return {Boolean}
33323      */
33324     isExpanded : function(){
33325         return this.expanded;
33326     },
33327
33328     /**
33329      * Returns the UI object for this node
33330      * @return {TreeNodeUI}
33331      */
33332     getUI : function(){
33333         return this.ui;
33334     },
33335
33336     // private override
33337     setFirstChild : function(node){
33338         var of = this.firstChild;
33339         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33340         if(this.childrenRendered && of && node != of){
33341             of.renderIndent(true, true);
33342         }
33343         if(this.rendered){
33344             this.renderIndent(true, true);
33345         }
33346     },
33347
33348     // private override
33349     setLastChild : function(node){
33350         var ol = this.lastChild;
33351         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33352         if(this.childrenRendered && ol && node != ol){
33353             ol.renderIndent(true, true);
33354         }
33355         if(this.rendered){
33356             this.renderIndent(true, true);
33357         }
33358     },
33359
33360     // these methods are overridden to provide lazy rendering support
33361     // private override
33362     appendChild : function()
33363     {
33364         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33365         if(node && this.childrenRendered){
33366             node.render();
33367         }
33368         this.ui.updateExpandIcon();
33369         return node;
33370     },
33371
33372     // private override
33373     removeChild : function(node){
33374         this.ownerTree.getSelectionModel().unselect(node);
33375         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33376         // if it's been rendered remove dom node
33377         if(this.childrenRendered){
33378             node.ui.remove();
33379         }
33380         if(this.childNodes.length < 1){
33381             this.collapse(false, false);
33382         }else{
33383             this.ui.updateExpandIcon();
33384         }
33385         if(!this.firstChild) {
33386             this.childrenRendered = false;
33387         }
33388         return node;
33389     },
33390
33391     // private override
33392     insertBefore : function(node, refNode){
33393         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33394         if(newNode && refNode && this.childrenRendered){
33395             node.render();
33396         }
33397         this.ui.updateExpandIcon();
33398         return newNode;
33399     },
33400
33401     /**
33402      * Sets the text for this node
33403      * @param {String} text
33404      */
33405     setText : function(text){
33406         var oldText = this.text;
33407         this.text = text;
33408         this.attributes.text = text;
33409         if(this.rendered){ // event without subscribing
33410             this.ui.onTextChange(this, text, oldText);
33411         }
33412         this.fireEvent("textchange", this, text, oldText);
33413     },
33414
33415     /**
33416      * Triggers selection of this node
33417      */
33418     select : function(){
33419         this.getOwnerTree().getSelectionModel().select(this);
33420     },
33421
33422     /**
33423      * Triggers deselection of this node
33424      */
33425     unselect : function(){
33426         this.getOwnerTree().getSelectionModel().unselect(this);
33427     },
33428
33429     /**
33430      * Returns true if this node is selected
33431      * @return {Boolean}
33432      */
33433     isSelected : function(){
33434         return this.getOwnerTree().getSelectionModel().isSelected(this);
33435     },
33436
33437     /**
33438      * Expand this node.
33439      * @param {Boolean} deep (optional) True to expand all children as well
33440      * @param {Boolean} anim (optional) false to cancel the default animation
33441      * @param {Function} callback (optional) A callback to be called when
33442      * expanding this node completes (does not wait for deep expand to complete).
33443      * Called with 1 parameter, this node.
33444      */
33445     expand : function(deep, anim, callback){
33446         if(!this.expanded){
33447             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33448                 return;
33449             }
33450             if(!this.childrenRendered){
33451                 this.renderChildren();
33452             }
33453             this.expanded = true;
33454             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33455                 this.ui.animExpand(function(){
33456                     this.fireEvent("expand", this);
33457                     if(typeof callback == "function"){
33458                         callback(this);
33459                     }
33460                     if(deep === true){
33461                         this.expandChildNodes(true);
33462                     }
33463                 }.createDelegate(this));
33464                 return;
33465             }else{
33466                 this.ui.expand();
33467                 this.fireEvent("expand", this);
33468                 if(typeof callback == "function"){
33469                     callback(this);
33470                 }
33471             }
33472         }else{
33473            if(typeof callback == "function"){
33474                callback(this);
33475            }
33476         }
33477         if(deep === true){
33478             this.expandChildNodes(true);
33479         }
33480     },
33481
33482     isHiddenRoot : function(){
33483         return this.isRoot && !this.getOwnerTree().rootVisible;
33484     },
33485
33486     /**
33487      * Collapse this node.
33488      * @param {Boolean} deep (optional) True to collapse all children as well
33489      * @param {Boolean} anim (optional) false to cancel the default animation
33490      */
33491     collapse : function(deep, anim){
33492         if(this.expanded && !this.isHiddenRoot()){
33493             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33494                 return;
33495             }
33496             this.expanded = false;
33497             if((this.getOwnerTree().animate && anim !== false) || anim){
33498                 this.ui.animCollapse(function(){
33499                     this.fireEvent("collapse", this);
33500                     if(deep === true){
33501                         this.collapseChildNodes(true);
33502                     }
33503                 }.createDelegate(this));
33504                 return;
33505             }else{
33506                 this.ui.collapse();
33507                 this.fireEvent("collapse", this);
33508             }
33509         }
33510         if(deep === true){
33511             var cs = this.childNodes;
33512             for(var i = 0, len = cs.length; i < len; i++) {
33513                 cs[i].collapse(true, false);
33514             }
33515         }
33516     },
33517
33518     // private
33519     delayedExpand : function(delay){
33520         if(!this.expandProcId){
33521             this.expandProcId = this.expand.defer(delay, this);
33522         }
33523     },
33524
33525     // private
33526     cancelExpand : function(){
33527         if(this.expandProcId){
33528             clearTimeout(this.expandProcId);
33529         }
33530         this.expandProcId = false;
33531     },
33532
33533     /**
33534      * Toggles expanded/collapsed state of the node
33535      */
33536     toggle : function(){
33537         if(this.expanded){
33538             this.collapse();
33539         }else{
33540             this.expand();
33541         }
33542     },
33543
33544     /**
33545      * Ensures all parent nodes are expanded
33546      */
33547     ensureVisible : function(callback){
33548         var tree = this.getOwnerTree();
33549         tree.expandPath(this.parentNode.getPath(), false, function(){
33550             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33551             Roo.callback(callback);
33552         }.createDelegate(this));
33553     },
33554
33555     /**
33556      * Expand all child nodes
33557      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33558      */
33559     expandChildNodes : function(deep){
33560         var cs = this.childNodes;
33561         for(var i = 0, len = cs.length; i < len; i++) {
33562                 cs[i].expand(deep);
33563         }
33564     },
33565
33566     /**
33567      * Collapse all child nodes
33568      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33569      */
33570     collapseChildNodes : function(deep){
33571         var cs = this.childNodes;
33572         for(var i = 0, len = cs.length; i < len; i++) {
33573                 cs[i].collapse(deep);
33574         }
33575     },
33576
33577     /**
33578      * Disables this node
33579      */
33580     disable : function(){
33581         this.disabled = true;
33582         this.unselect();
33583         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33584             this.ui.onDisableChange(this, true);
33585         }
33586         this.fireEvent("disabledchange", this, true);
33587     },
33588
33589     /**
33590      * Enables this node
33591      */
33592     enable : function(){
33593         this.disabled = false;
33594         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33595             this.ui.onDisableChange(this, false);
33596         }
33597         this.fireEvent("disabledchange", this, false);
33598     },
33599
33600     // private
33601     renderChildren : function(suppressEvent){
33602         if(suppressEvent !== false){
33603             this.fireEvent("beforechildrenrendered", this);
33604         }
33605         var cs = this.childNodes;
33606         for(var i = 0, len = cs.length; i < len; i++){
33607             cs[i].render(true);
33608         }
33609         this.childrenRendered = true;
33610     },
33611
33612     // private
33613     sort : function(fn, scope){
33614         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33615         if(this.childrenRendered){
33616             var cs = this.childNodes;
33617             for(var i = 0, len = cs.length; i < len; i++){
33618                 cs[i].render(true);
33619             }
33620         }
33621     },
33622
33623     // private
33624     render : function(bulkRender){
33625         this.ui.render(bulkRender);
33626         if(!this.rendered){
33627             this.rendered = true;
33628             if(this.expanded){
33629                 this.expanded = false;
33630                 this.expand(false, false);
33631             }
33632         }
33633     },
33634
33635     // private
33636     renderIndent : function(deep, refresh){
33637         if(refresh){
33638             this.ui.childIndent = null;
33639         }
33640         this.ui.renderIndent();
33641         if(deep === true && this.childrenRendered){
33642             var cs = this.childNodes;
33643             for(var i = 0, len = cs.length; i < len; i++){
33644                 cs[i].renderIndent(true, refresh);
33645             }
33646         }
33647     }
33648 });/*
33649  * Based on:
33650  * Ext JS Library 1.1.1
33651  * Copyright(c) 2006-2007, Ext JS, LLC.
33652  *
33653  * Originally Released Under LGPL - original licence link has changed is not relivant.
33654  *
33655  * Fork - LGPL
33656  * <script type="text/javascript">
33657  */
33658  
33659 /**
33660  * @class Roo.tree.AsyncTreeNode
33661  * @extends Roo.tree.TreeNode
33662  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33663  * @constructor
33664  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33665  */
33666  Roo.tree.AsyncTreeNode = function(config){
33667     this.loaded = false;
33668     this.loading = false;
33669     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33670     /**
33671     * @event beforeload
33672     * Fires before this node is loaded, return false to cancel
33673     * @param {Node} this This node
33674     */
33675     this.addEvents({'beforeload':true, 'load': true});
33676     /**
33677     * @event load
33678     * Fires when this node is loaded
33679     * @param {Node} this This node
33680     */
33681     /**
33682      * The loader used by this node (defaults to using the tree's defined loader)
33683      * @type TreeLoader
33684      * @property loader
33685      */
33686 };
33687 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33688     expand : function(deep, anim, callback){
33689         if(this.loading){ // if an async load is already running, waiting til it's done
33690             var timer;
33691             var f = function(){
33692                 if(!this.loading){ // done loading
33693                     clearInterval(timer);
33694                     this.expand(deep, anim, callback);
33695                 }
33696             }.createDelegate(this);
33697             timer = setInterval(f, 200);
33698             return;
33699         }
33700         if(!this.loaded){
33701             if(this.fireEvent("beforeload", this) === false){
33702                 return;
33703             }
33704             this.loading = true;
33705             this.ui.beforeLoad(this);
33706             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33707             if(loader){
33708                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33709                 return;
33710             }
33711         }
33712         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33713     },
33714     
33715     /**
33716      * Returns true if this node is currently loading
33717      * @return {Boolean}
33718      */
33719     isLoading : function(){
33720         return this.loading;  
33721     },
33722     
33723     loadComplete : function(deep, anim, callback){
33724         this.loading = false;
33725         this.loaded = true;
33726         this.ui.afterLoad(this);
33727         this.fireEvent("load", this);
33728         this.expand(deep, anim, callback);
33729     },
33730     
33731     /**
33732      * Returns true if this node has been loaded
33733      * @return {Boolean}
33734      */
33735     isLoaded : function(){
33736         return this.loaded;
33737     },
33738     
33739     hasChildNodes : function(){
33740         if(!this.isLeaf() && !this.loaded){
33741             return true;
33742         }else{
33743             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33744         }
33745     },
33746
33747     /**
33748      * Trigger a reload for this node
33749      * @param {Function} callback
33750      */
33751     reload : function(callback){
33752         this.collapse(false, false);
33753         while(this.firstChild){
33754             this.removeChild(this.firstChild);
33755         }
33756         this.childrenRendered = false;
33757         this.loaded = false;
33758         if(this.isHiddenRoot()){
33759             this.expanded = false;
33760         }
33761         this.expand(false, false, callback);
33762     }
33763 });/*
33764  * Based on:
33765  * Ext JS Library 1.1.1
33766  * Copyright(c) 2006-2007, Ext JS, LLC.
33767  *
33768  * Originally Released Under LGPL - original licence link has changed is not relivant.
33769  *
33770  * Fork - LGPL
33771  * <script type="text/javascript">
33772  */
33773  
33774 /**
33775  * @class Roo.tree.TreeNodeUI
33776  * @constructor
33777  * @param {Object} node The node to render
33778  * The TreeNode UI implementation is separate from the
33779  * tree implementation. Unless you are customizing the tree UI,
33780  * you should never have to use this directly.
33781  */
33782 Roo.tree.TreeNodeUI = function(node){
33783     this.node = node;
33784     this.rendered = false;
33785     this.animating = false;
33786     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33787 };
33788
33789 Roo.tree.TreeNodeUI.prototype = {
33790     removeChild : function(node){
33791         if(this.rendered){
33792             this.ctNode.removeChild(node.ui.getEl());
33793         }
33794     },
33795
33796     beforeLoad : function(){
33797          this.addClass("x-tree-node-loading");
33798     },
33799
33800     afterLoad : function(){
33801          this.removeClass("x-tree-node-loading");
33802     },
33803
33804     onTextChange : function(node, text, oldText){
33805         if(this.rendered){
33806             this.textNode.innerHTML = text;
33807         }
33808     },
33809
33810     onDisableChange : function(node, state){
33811         this.disabled = state;
33812         if(state){
33813             this.addClass("x-tree-node-disabled");
33814         }else{
33815             this.removeClass("x-tree-node-disabled");
33816         }
33817     },
33818
33819     onSelectedChange : function(state){
33820         if(state){
33821             this.focus();
33822             this.addClass("x-tree-selected");
33823         }else{
33824             //this.blur();
33825             this.removeClass("x-tree-selected");
33826         }
33827     },
33828
33829     onMove : function(tree, node, oldParent, newParent, index, refNode){
33830         this.childIndent = null;
33831         if(this.rendered){
33832             var targetNode = newParent.ui.getContainer();
33833             if(!targetNode){//target not rendered
33834                 this.holder = document.createElement("div");
33835                 this.holder.appendChild(this.wrap);
33836                 return;
33837             }
33838             var insertBefore = refNode ? refNode.ui.getEl() : null;
33839             if(insertBefore){
33840                 targetNode.insertBefore(this.wrap, insertBefore);
33841             }else{
33842                 targetNode.appendChild(this.wrap);
33843             }
33844             this.node.renderIndent(true);
33845         }
33846     },
33847
33848     addClass : function(cls){
33849         if(this.elNode){
33850             Roo.fly(this.elNode).addClass(cls);
33851         }
33852     },
33853
33854     removeClass : function(cls){
33855         if(this.elNode){
33856             Roo.fly(this.elNode).removeClass(cls);
33857         }
33858     },
33859
33860     remove : function(){
33861         if(this.rendered){
33862             this.holder = document.createElement("div");
33863             this.holder.appendChild(this.wrap);
33864         }
33865     },
33866
33867     fireEvent : function(){
33868         return this.node.fireEvent.apply(this.node, arguments);
33869     },
33870
33871     initEvents : function(){
33872         this.node.on("move", this.onMove, this);
33873         var E = Roo.EventManager;
33874         var a = this.anchor;
33875
33876         var el = Roo.fly(a, '_treeui');
33877
33878         if(Roo.isOpera){ // opera render bug ignores the CSS
33879             el.setStyle("text-decoration", "none");
33880         }
33881
33882         el.on("click", this.onClick, this);
33883         el.on("dblclick", this.onDblClick, this);
33884
33885         if(this.checkbox){
33886             Roo.EventManager.on(this.checkbox,
33887                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33888         }
33889
33890         el.on("contextmenu", this.onContextMenu, this);
33891
33892         var icon = Roo.fly(this.iconNode);
33893         icon.on("click", this.onClick, this);
33894         icon.on("dblclick", this.onDblClick, this);
33895         icon.on("contextmenu", this.onContextMenu, this);
33896         E.on(this.ecNode, "click", this.ecClick, this, true);
33897
33898         if(this.node.disabled){
33899             this.addClass("x-tree-node-disabled");
33900         }
33901         if(this.node.hidden){
33902             this.addClass("x-tree-node-disabled");
33903         }
33904         var ot = this.node.getOwnerTree();
33905         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33906         if(dd && (!this.node.isRoot || ot.rootVisible)){
33907             Roo.dd.Registry.register(this.elNode, {
33908                 node: this.node,
33909                 handles: this.getDDHandles(),
33910                 isHandle: false
33911             });
33912         }
33913     },
33914
33915     getDDHandles : function(){
33916         return [this.iconNode, this.textNode];
33917     },
33918
33919     hide : function(){
33920         if(this.rendered){
33921             this.wrap.style.display = "none";
33922         }
33923     },
33924
33925     show : function(){
33926         if(this.rendered){
33927             this.wrap.style.display = "";
33928         }
33929     },
33930
33931     onContextMenu : function(e){
33932         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33933             e.preventDefault();
33934             this.focus();
33935             this.fireEvent("contextmenu", this.node, e);
33936         }
33937     },
33938
33939     onClick : function(e){
33940         if(this.dropping){
33941             e.stopEvent();
33942             return;
33943         }
33944         if(this.fireEvent("beforeclick", this.node, e) !== false){
33945             if(!this.disabled && this.node.attributes.href){
33946                 this.fireEvent("click", this.node, e);
33947                 return;
33948             }
33949             e.preventDefault();
33950             if(this.disabled){
33951                 return;
33952             }
33953
33954             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33955                 this.node.toggle();
33956             }
33957
33958             this.fireEvent("click", this.node, e);
33959         }else{
33960             e.stopEvent();
33961         }
33962     },
33963
33964     onDblClick : function(e){
33965         e.preventDefault();
33966         if(this.disabled){
33967             return;
33968         }
33969         if(this.checkbox){
33970             this.toggleCheck();
33971         }
33972         if(!this.animating && this.node.hasChildNodes()){
33973             this.node.toggle();
33974         }
33975         this.fireEvent("dblclick", this.node, e);
33976     },
33977
33978     onCheckChange : function(){
33979         var checked = this.checkbox.checked;
33980         this.node.attributes.checked = checked;
33981         this.fireEvent('checkchange', this.node, checked);
33982     },
33983
33984     ecClick : function(e){
33985         if(!this.animating && this.node.hasChildNodes()){
33986             this.node.toggle();
33987         }
33988     },
33989
33990     startDrop : function(){
33991         this.dropping = true;
33992     },
33993
33994     // delayed drop so the click event doesn't get fired on a drop
33995     endDrop : function(){
33996        setTimeout(function(){
33997            this.dropping = false;
33998        }.createDelegate(this), 50);
33999     },
34000
34001     expand : function(){
34002         this.updateExpandIcon();
34003         this.ctNode.style.display = "";
34004     },
34005
34006     focus : function(){
34007         if(!this.node.preventHScroll){
34008             try{this.anchor.focus();
34009             }catch(e){}
34010         }else if(!Roo.isIE){
34011             try{
34012                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34013                 var l = noscroll.scrollLeft;
34014                 this.anchor.focus();
34015                 noscroll.scrollLeft = l;
34016             }catch(e){}
34017         }
34018     },
34019
34020     toggleCheck : function(value){
34021         var cb = this.checkbox;
34022         if(cb){
34023             cb.checked = (value === undefined ? !cb.checked : value);
34024         }
34025     },
34026
34027     blur : function(){
34028         try{
34029             this.anchor.blur();
34030         }catch(e){}
34031     },
34032
34033     animExpand : function(callback){
34034         var ct = Roo.get(this.ctNode);
34035         ct.stopFx();
34036         if(!this.node.hasChildNodes()){
34037             this.updateExpandIcon();
34038             this.ctNode.style.display = "";
34039             Roo.callback(callback);
34040             return;
34041         }
34042         this.animating = true;
34043         this.updateExpandIcon();
34044
34045         ct.slideIn('t', {
34046            callback : function(){
34047                this.animating = false;
34048                Roo.callback(callback);
34049             },
34050             scope: this,
34051             duration: this.node.ownerTree.duration || .25
34052         });
34053     },
34054
34055     highlight : function(){
34056         var tree = this.node.getOwnerTree();
34057         Roo.fly(this.wrap).highlight(
34058             tree.hlColor || "C3DAF9",
34059             {endColor: tree.hlBaseColor}
34060         );
34061     },
34062
34063     collapse : function(){
34064         this.updateExpandIcon();
34065         this.ctNode.style.display = "none";
34066     },
34067
34068     animCollapse : function(callback){
34069         var ct = Roo.get(this.ctNode);
34070         ct.enableDisplayMode('block');
34071         ct.stopFx();
34072
34073         this.animating = true;
34074         this.updateExpandIcon();
34075
34076         ct.slideOut('t', {
34077             callback : function(){
34078                this.animating = false;
34079                Roo.callback(callback);
34080             },
34081             scope: this,
34082             duration: this.node.ownerTree.duration || .25
34083         });
34084     },
34085
34086     getContainer : function(){
34087         return this.ctNode;
34088     },
34089
34090     getEl : function(){
34091         return this.wrap;
34092     },
34093
34094     appendDDGhost : function(ghostNode){
34095         ghostNode.appendChild(this.elNode.cloneNode(true));
34096     },
34097
34098     getDDRepairXY : function(){
34099         return Roo.lib.Dom.getXY(this.iconNode);
34100     },
34101
34102     onRender : function(){
34103         this.render();
34104     },
34105
34106     render : function(bulkRender){
34107         var n = this.node, a = n.attributes;
34108         var targetNode = n.parentNode ?
34109               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34110
34111         if(!this.rendered){
34112             this.rendered = true;
34113
34114             this.renderElements(n, a, targetNode, bulkRender);
34115
34116             if(a.qtip){
34117                if(this.textNode.setAttributeNS){
34118                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34119                    if(a.qtipTitle){
34120                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34121                    }
34122                }else{
34123                    this.textNode.setAttribute("ext:qtip", a.qtip);
34124                    if(a.qtipTitle){
34125                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34126                    }
34127                }
34128             }else if(a.qtipCfg){
34129                 a.qtipCfg.target = Roo.id(this.textNode);
34130                 Roo.QuickTips.register(a.qtipCfg);
34131             }
34132             this.initEvents();
34133             if(!this.node.expanded){
34134                 this.updateExpandIcon();
34135             }
34136         }else{
34137             if(bulkRender === true) {
34138                 targetNode.appendChild(this.wrap);
34139             }
34140         }
34141     },
34142
34143     renderElements : function(n, a, targetNode, bulkRender)
34144     {
34145         // add some indent caching, this helps performance when rendering a large tree
34146         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34147         var t = n.getOwnerTree();
34148         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34149         if (typeof(n.attributes.html) != 'undefined') {
34150             txt = n.attributes.html;
34151         }
34152         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34153         var cb = typeof a.checked == 'boolean';
34154         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34155         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34156             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34157             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34158             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34159             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34160             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34161              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34162                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34163             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34164             "</li>"];
34165
34166         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34167             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34168                                 n.nextSibling.ui.getEl(), buf.join(""));
34169         }else{
34170             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34171         }
34172
34173         this.elNode = this.wrap.childNodes[0];
34174         this.ctNode = this.wrap.childNodes[1];
34175         var cs = this.elNode.childNodes;
34176         this.indentNode = cs[0];
34177         this.ecNode = cs[1];
34178         this.iconNode = cs[2];
34179         var index = 3;
34180         if(cb){
34181             this.checkbox = cs[3];
34182             index++;
34183         }
34184         this.anchor = cs[index];
34185         this.textNode = cs[index].firstChild;
34186     },
34187
34188     getAnchor : function(){
34189         return this.anchor;
34190     },
34191
34192     getTextEl : function(){
34193         return this.textNode;
34194     },
34195
34196     getIconEl : function(){
34197         return this.iconNode;
34198     },
34199
34200     isChecked : function(){
34201         return this.checkbox ? this.checkbox.checked : false;
34202     },
34203
34204     updateExpandIcon : function(){
34205         if(this.rendered){
34206             var n = this.node, c1, c2;
34207             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34208             var hasChild = n.hasChildNodes();
34209             if(hasChild){
34210                 if(n.expanded){
34211                     cls += "-minus";
34212                     c1 = "x-tree-node-collapsed";
34213                     c2 = "x-tree-node-expanded";
34214                 }else{
34215                     cls += "-plus";
34216                     c1 = "x-tree-node-expanded";
34217                     c2 = "x-tree-node-collapsed";
34218                 }
34219                 if(this.wasLeaf){
34220                     this.removeClass("x-tree-node-leaf");
34221                     this.wasLeaf = false;
34222                 }
34223                 if(this.c1 != c1 || this.c2 != c2){
34224                     Roo.fly(this.elNode).replaceClass(c1, c2);
34225                     this.c1 = c1; this.c2 = c2;
34226                 }
34227             }else{
34228                 // this changes non-leafs into leafs if they have no children.
34229                 // it's not very rational behaviour..
34230                 
34231                 if(!this.wasLeaf && this.node.leaf){
34232                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34233                     delete this.c1;
34234                     delete this.c2;
34235                     this.wasLeaf = true;
34236                 }
34237             }
34238             var ecc = "x-tree-ec-icon "+cls;
34239             if(this.ecc != ecc){
34240                 this.ecNode.className = ecc;
34241                 this.ecc = ecc;
34242             }
34243         }
34244     },
34245
34246     getChildIndent : function(){
34247         if(!this.childIndent){
34248             var buf = [];
34249             var p = this.node;
34250             while(p){
34251                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34252                     if(!p.isLast()) {
34253                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34254                     } else {
34255                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34256                     }
34257                 }
34258                 p = p.parentNode;
34259             }
34260             this.childIndent = buf.join("");
34261         }
34262         return this.childIndent;
34263     },
34264
34265     renderIndent : function(){
34266         if(this.rendered){
34267             var indent = "";
34268             var p = this.node.parentNode;
34269             if(p){
34270                 indent = p.ui.getChildIndent();
34271             }
34272             if(this.indentMarkup != indent){ // don't rerender if not required
34273                 this.indentNode.innerHTML = indent;
34274                 this.indentMarkup = indent;
34275             }
34276             this.updateExpandIcon();
34277         }
34278     }
34279 };
34280
34281 Roo.tree.RootTreeNodeUI = function(){
34282     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34283 };
34284 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34285     render : function(){
34286         if(!this.rendered){
34287             var targetNode = this.node.ownerTree.innerCt.dom;
34288             this.node.expanded = true;
34289             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34290             this.wrap = this.ctNode = targetNode.firstChild;
34291         }
34292     },
34293     collapse : function(){
34294     },
34295     expand : function(){
34296     }
34297 });/*
34298  * Based on:
34299  * Ext JS Library 1.1.1
34300  * Copyright(c) 2006-2007, Ext JS, LLC.
34301  *
34302  * Originally Released Under LGPL - original licence link has changed is not relivant.
34303  *
34304  * Fork - LGPL
34305  * <script type="text/javascript">
34306  */
34307 /**
34308  * @class Roo.tree.TreeLoader
34309  * @extends Roo.util.Observable
34310  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34311  * nodes from a specified URL. The response must be a javascript Array definition
34312  * who's elements are node definition objects. eg:
34313  * <pre><code>
34314 {  success : true,
34315    data :      [
34316    
34317     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34318     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34319     ]
34320 }
34321
34322
34323 </code></pre>
34324  * <br><br>
34325  * The old style respose with just an array is still supported, but not recommended.
34326  * <br><br>
34327  *
34328  * A server request is sent, and child nodes are loaded only when a node is expanded.
34329  * The loading node's id is passed to the server under the parameter name "node" to
34330  * enable the server to produce the correct child nodes.
34331  * <br><br>
34332  * To pass extra parameters, an event handler may be attached to the "beforeload"
34333  * event, and the parameters specified in the TreeLoader's baseParams property:
34334  * <pre><code>
34335     myTreeLoader.on("beforeload", function(treeLoader, node) {
34336         this.baseParams.category = node.attributes.category;
34337     }, this);
34338 </code></pre><
34339  * This would pass an HTTP parameter called "category" to the server containing
34340  * the value of the Node's "category" attribute.
34341  * @constructor
34342  * Creates a new Treeloader.
34343  * @param {Object} config A config object containing config properties.
34344  */
34345 Roo.tree.TreeLoader = function(config){
34346     this.baseParams = {};
34347     this.requestMethod = "POST";
34348     Roo.apply(this, config);
34349
34350     this.addEvents({
34351     
34352         /**
34353          * @event beforeload
34354          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34355          * @param {Object} This TreeLoader object.
34356          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34357          * @param {Object} callback The callback function specified in the {@link #load} call.
34358          */
34359         beforeload : true,
34360         /**
34361          * @event load
34362          * Fires when the node has been successfuly loaded.
34363          * @param {Object} This TreeLoader object.
34364          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34365          * @param {Object} response The response object containing the data from the server.
34366          */
34367         load : true,
34368         /**
34369          * @event loadexception
34370          * Fires if the network request failed.
34371          * @param {Object} This TreeLoader object.
34372          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34373          * @param {Object} response The response object containing the data from the server.
34374          */
34375         loadexception : true,
34376         /**
34377          * @event create
34378          * Fires before a node is created, enabling you to return custom Node types 
34379          * @param {Object} This TreeLoader object.
34380          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34381          */
34382         create : true
34383     });
34384
34385     Roo.tree.TreeLoader.superclass.constructor.call(this);
34386 };
34387
34388 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34389     /**
34390     * @cfg {String} dataUrl The URL from which to request a Json string which
34391     * specifies an array of node definition object representing the child nodes
34392     * to be loaded.
34393     */
34394     /**
34395     * @cfg {String} requestMethod either GET or POST
34396     * defaults to POST (due to BC)
34397     * to be loaded.
34398     */
34399     /**
34400     * @cfg {Object} baseParams (optional) An object containing properties which
34401     * specify HTTP parameters to be passed to each request for child nodes.
34402     */
34403     /**
34404     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34405     * created by this loader. If the attributes sent by the server have an attribute in this object,
34406     * they take priority.
34407     */
34408     /**
34409     * @cfg {Object} uiProviders (optional) An object containing properties which
34410     * 
34411     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34412     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34413     * <i>uiProvider</i> attribute of a returned child node is a string rather
34414     * than a reference to a TreeNodeUI implementation, this that string value
34415     * is used as a property name in the uiProviders object. You can define the provider named
34416     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34417     */
34418     uiProviders : {},
34419
34420     /**
34421     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34422     * child nodes before loading.
34423     */
34424     clearOnLoad : true,
34425
34426     /**
34427     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34428     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34429     * Grid query { data : [ .....] }
34430     */
34431     
34432     root : false,
34433      /**
34434     * @cfg {String} queryParam (optional) 
34435     * Name of the query as it will be passed on the querystring (defaults to 'node')
34436     * eg. the request will be ?node=[id]
34437     */
34438     
34439     
34440     queryParam: false,
34441     
34442     /**
34443      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34444      * This is called automatically when a node is expanded, but may be used to reload
34445      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34446      * @param {Roo.tree.TreeNode} node
34447      * @param {Function} callback
34448      */
34449     load : function(node, callback){
34450         if(this.clearOnLoad){
34451             while(node.firstChild){
34452                 node.removeChild(node.firstChild);
34453             }
34454         }
34455         if(node.attributes.children){ // preloaded json children
34456             var cs = node.attributes.children;
34457             for(var i = 0, len = cs.length; i < len; i++){
34458                 node.appendChild(this.createNode(cs[i]));
34459             }
34460             if(typeof callback == "function"){
34461                 callback();
34462             }
34463         }else if(this.dataUrl){
34464             this.requestData(node, callback);
34465         }
34466     },
34467
34468     getParams: function(node){
34469         var buf = [], bp = this.baseParams;
34470         for(var key in bp){
34471             if(typeof bp[key] != "function"){
34472                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34473             }
34474         }
34475         var n = this.queryParam === false ? 'node' : this.queryParam;
34476         buf.push(n + "=", encodeURIComponent(node.id));
34477         return buf.join("");
34478     },
34479
34480     requestData : function(node, callback){
34481         if(this.fireEvent("beforeload", this, node, callback) !== false){
34482             this.transId = Roo.Ajax.request({
34483                 method:this.requestMethod,
34484                 url: this.dataUrl||this.url,
34485                 success: this.handleResponse,
34486                 failure: this.handleFailure,
34487                 scope: this,
34488                 argument: {callback: callback, node: node},
34489                 params: this.getParams(node)
34490             });
34491         }else{
34492             // if the load is cancelled, make sure we notify
34493             // the node that we are done
34494             if(typeof callback == "function"){
34495                 callback();
34496             }
34497         }
34498     },
34499
34500     isLoading : function(){
34501         return this.transId ? true : false;
34502     },
34503
34504     abort : function(){
34505         if(this.isLoading()){
34506             Roo.Ajax.abort(this.transId);
34507         }
34508     },
34509
34510     // private
34511     createNode : function(attr)
34512     {
34513         // apply baseAttrs, nice idea Corey!
34514         if(this.baseAttrs){
34515             Roo.applyIf(attr, this.baseAttrs);
34516         }
34517         if(this.applyLoader !== false){
34518             attr.loader = this;
34519         }
34520         // uiProvider = depreciated..
34521         
34522         if(typeof(attr.uiProvider) == 'string'){
34523            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34524                 /**  eval:var:attr */ eval(attr.uiProvider);
34525         }
34526         if(typeof(this.uiProviders['default']) != 'undefined') {
34527             attr.uiProvider = this.uiProviders['default'];
34528         }
34529         
34530         this.fireEvent('create', this, attr);
34531         
34532         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34533         return(attr.leaf ?
34534                         new Roo.tree.TreeNode(attr) :
34535                         new Roo.tree.AsyncTreeNode(attr));
34536     },
34537
34538     processResponse : function(response, node, callback)
34539     {
34540         var json = response.responseText;
34541         try {
34542             
34543             var o = Roo.decode(json);
34544             
34545             if (this.root === false && typeof(o.success) != undefined) {
34546                 this.root = 'data'; // the default behaviour for list like data..
34547                 }
34548                 
34549             if (this.root !== false &&  !o.success) {
34550                 // it's a failure condition.
34551                 var a = response.argument;
34552                 this.fireEvent("loadexception", this, a.node, response);
34553                 Roo.log("Load failed - should have a handler really");
34554                 return;
34555             }
34556             
34557             
34558             
34559             if (this.root !== false) {
34560                  o = o[this.root];
34561             }
34562             
34563             for(var i = 0, len = o.length; i < len; i++){
34564                 var n = this.createNode(o[i]);
34565                 if(n){
34566                     node.appendChild(n);
34567                 }
34568             }
34569             if(typeof callback == "function"){
34570                 callback(this, node);
34571             }
34572         }catch(e){
34573             this.handleFailure(response);
34574         }
34575     },
34576
34577     handleResponse : function(response){
34578         this.transId = false;
34579         var a = response.argument;
34580         this.processResponse(response, a.node, a.callback);
34581         this.fireEvent("load", this, a.node, response);
34582     },
34583
34584     handleFailure : function(response)
34585     {
34586         // should handle failure better..
34587         this.transId = false;
34588         var a = response.argument;
34589         this.fireEvent("loadexception", this, a.node, response);
34590         if(typeof a.callback == "function"){
34591             a.callback(this, a.node);
34592         }
34593     }
34594 });/*
34595  * Based on:
34596  * Ext JS Library 1.1.1
34597  * Copyright(c) 2006-2007, Ext JS, LLC.
34598  *
34599  * Originally Released Under LGPL - original licence link has changed is not relivant.
34600  *
34601  * Fork - LGPL
34602  * <script type="text/javascript">
34603  */
34604
34605 /**
34606 * @class Roo.tree.TreeFilter
34607 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34608 * @param {TreePanel} tree
34609 * @param {Object} config (optional)
34610  */
34611 Roo.tree.TreeFilter = function(tree, config){
34612     this.tree = tree;
34613     this.filtered = {};
34614     Roo.apply(this, config);
34615 };
34616
34617 Roo.tree.TreeFilter.prototype = {
34618     clearBlank:false,
34619     reverse:false,
34620     autoClear:false,
34621     remove:false,
34622
34623      /**
34624      * Filter the data by a specific attribute.
34625      * @param {String/RegExp} value Either string that the attribute value
34626      * should start with or a RegExp to test against the attribute
34627      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34628      * @param {TreeNode} startNode (optional) The node to start the filter at.
34629      */
34630     filter : function(value, attr, startNode){
34631         attr = attr || "text";
34632         var f;
34633         if(typeof value == "string"){
34634             var vlen = value.length;
34635             // auto clear empty filter
34636             if(vlen == 0 && this.clearBlank){
34637                 this.clear();
34638                 return;
34639             }
34640             value = value.toLowerCase();
34641             f = function(n){
34642                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34643             };
34644         }else if(value.exec){ // regex?
34645             f = function(n){
34646                 return value.test(n.attributes[attr]);
34647             };
34648         }else{
34649             throw 'Illegal filter type, must be string or regex';
34650         }
34651         this.filterBy(f, null, startNode);
34652         },
34653
34654     /**
34655      * Filter by a function. The passed function will be called with each
34656      * node in the tree (or from the startNode). If the function returns true, the node is kept
34657      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34658      * @param {Function} fn The filter function
34659      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34660      */
34661     filterBy : function(fn, scope, startNode){
34662         startNode = startNode || this.tree.root;
34663         if(this.autoClear){
34664             this.clear();
34665         }
34666         var af = this.filtered, rv = this.reverse;
34667         var f = function(n){
34668             if(n == startNode){
34669                 return true;
34670             }
34671             if(af[n.id]){
34672                 return false;
34673             }
34674             var m = fn.call(scope || n, n);
34675             if(!m || rv){
34676                 af[n.id] = n;
34677                 n.ui.hide();
34678                 return false;
34679             }
34680             return true;
34681         };
34682         startNode.cascade(f);
34683         if(this.remove){
34684            for(var id in af){
34685                if(typeof id != "function"){
34686                    var n = af[id];
34687                    if(n && n.parentNode){
34688                        n.parentNode.removeChild(n);
34689                    }
34690                }
34691            }
34692         }
34693     },
34694
34695     /**
34696      * Clears the current filter. Note: with the "remove" option
34697      * set a filter cannot be cleared.
34698      */
34699     clear : function(){
34700         var t = this.tree;
34701         var af = this.filtered;
34702         for(var id in af){
34703             if(typeof id != "function"){
34704                 var n = af[id];
34705                 if(n){
34706                     n.ui.show();
34707                 }
34708             }
34709         }
34710         this.filtered = {};
34711     }
34712 };
34713 /*
34714  * Based on:
34715  * Ext JS Library 1.1.1
34716  * Copyright(c) 2006-2007, Ext JS, LLC.
34717  *
34718  * Originally Released Under LGPL - original licence link has changed is not relivant.
34719  *
34720  * Fork - LGPL
34721  * <script type="text/javascript">
34722  */
34723  
34724
34725 /**
34726  * @class Roo.tree.TreeSorter
34727  * Provides sorting of nodes in a TreePanel
34728  * 
34729  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34730  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34731  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34732  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34733  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34734  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34735  * @constructor
34736  * @param {TreePanel} tree
34737  * @param {Object} config
34738  */
34739 Roo.tree.TreeSorter = function(tree, config){
34740     Roo.apply(this, config);
34741     tree.on("beforechildrenrendered", this.doSort, this);
34742     tree.on("append", this.updateSort, this);
34743     tree.on("insert", this.updateSort, this);
34744     
34745     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34746     var p = this.property || "text";
34747     var sortType = this.sortType;
34748     var fs = this.folderSort;
34749     var cs = this.caseSensitive === true;
34750     var leafAttr = this.leafAttr || 'leaf';
34751
34752     this.sortFn = function(n1, n2){
34753         if(fs){
34754             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34755                 return 1;
34756             }
34757             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34758                 return -1;
34759             }
34760         }
34761         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34762         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34763         if(v1 < v2){
34764                         return dsc ? +1 : -1;
34765                 }else if(v1 > v2){
34766                         return dsc ? -1 : +1;
34767         }else{
34768                 return 0;
34769         }
34770     };
34771 };
34772
34773 Roo.tree.TreeSorter.prototype = {
34774     doSort : function(node){
34775         node.sort(this.sortFn);
34776     },
34777     
34778     compareNodes : function(n1, n2){
34779         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34780     },
34781     
34782     updateSort : function(tree, node){
34783         if(node.childrenRendered){
34784             this.doSort.defer(1, this, [node]);
34785         }
34786     }
34787 };/*
34788  * Based on:
34789  * Ext JS Library 1.1.1
34790  * Copyright(c) 2006-2007, Ext JS, LLC.
34791  *
34792  * Originally Released Under LGPL - original licence link has changed is not relivant.
34793  *
34794  * Fork - LGPL
34795  * <script type="text/javascript">
34796  */
34797
34798 if(Roo.dd.DropZone){
34799     
34800 Roo.tree.TreeDropZone = function(tree, config){
34801     this.allowParentInsert = false;
34802     this.allowContainerDrop = false;
34803     this.appendOnly = false;
34804     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34805     this.tree = tree;
34806     this.lastInsertClass = "x-tree-no-status";
34807     this.dragOverData = {};
34808 };
34809
34810 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34811     ddGroup : "TreeDD",
34812     scroll:  true,
34813     
34814     expandDelay : 1000,
34815     
34816     expandNode : function(node){
34817         if(node.hasChildNodes() && !node.isExpanded()){
34818             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34819         }
34820     },
34821     
34822     queueExpand : function(node){
34823         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34824     },
34825     
34826     cancelExpand : function(){
34827         if(this.expandProcId){
34828             clearTimeout(this.expandProcId);
34829             this.expandProcId = false;
34830         }
34831     },
34832     
34833     isValidDropPoint : function(n, pt, dd, e, data){
34834         if(!n || !data){ return false; }
34835         var targetNode = n.node;
34836         var dropNode = data.node;
34837         // default drop rules
34838         if(!(targetNode && targetNode.isTarget && pt)){
34839             return false;
34840         }
34841         if(pt == "append" && targetNode.allowChildren === false){
34842             return false;
34843         }
34844         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34845             return false;
34846         }
34847         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34848             return false;
34849         }
34850         // reuse the object
34851         var overEvent = this.dragOverData;
34852         overEvent.tree = this.tree;
34853         overEvent.target = targetNode;
34854         overEvent.data = data;
34855         overEvent.point = pt;
34856         overEvent.source = dd;
34857         overEvent.rawEvent = e;
34858         overEvent.dropNode = dropNode;
34859         overEvent.cancel = false;  
34860         var result = this.tree.fireEvent("nodedragover", overEvent);
34861         return overEvent.cancel === false && result !== false;
34862     },
34863     
34864     getDropPoint : function(e, n, dd)
34865     {
34866         var tn = n.node;
34867         if(tn.isRoot){
34868             return tn.allowChildren !== false ? "append" : false; // always append for root
34869         }
34870         var dragEl = n.ddel;
34871         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34872         var y = Roo.lib.Event.getPageY(e);
34873         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34874         
34875         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34876         var noAppend = tn.allowChildren === false;
34877         if(this.appendOnly || tn.parentNode.allowChildren === false){
34878             return noAppend ? false : "append";
34879         }
34880         var noBelow = false;
34881         if(!this.allowParentInsert){
34882             noBelow = tn.hasChildNodes() && tn.isExpanded();
34883         }
34884         var q = (b - t) / (noAppend ? 2 : 3);
34885         if(y >= t && y < (t + q)){
34886             return "above";
34887         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34888             return "below";
34889         }else{
34890             return "append";
34891         }
34892     },
34893     
34894     onNodeEnter : function(n, dd, e, data)
34895     {
34896         this.cancelExpand();
34897     },
34898     
34899     onNodeOver : function(n, dd, e, data)
34900     {
34901        
34902         var pt = this.getDropPoint(e, n, dd);
34903         var node = n.node;
34904         
34905         // auto node expand check
34906         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34907             this.queueExpand(node);
34908         }else if(pt != "append"){
34909             this.cancelExpand();
34910         }
34911         
34912         // set the insert point style on the target node
34913         var returnCls = this.dropNotAllowed;
34914         if(this.isValidDropPoint(n, pt, dd, e, data)){
34915            if(pt){
34916                var el = n.ddel;
34917                var cls;
34918                if(pt == "above"){
34919                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34920                    cls = "x-tree-drag-insert-above";
34921                }else if(pt == "below"){
34922                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34923                    cls = "x-tree-drag-insert-below";
34924                }else{
34925                    returnCls = "x-tree-drop-ok-append";
34926                    cls = "x-tree-drag-append";
34927                }
34928                if(this.lastInsertClass != cls){
34929                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34930                    this.lastInsertClass = cls;
34931                }
34932            }
34933        }
34934        return returnCls;
34935     },
34936     
34937     onNodeOut : function(n, dd, e, data){
34938         
34939         this.cancelExpand();
34940         this.removeDropIndicators(n);
34941     },
34942     
34943     onNodeDrop : function(n, dd, e, data){
34944         var point = this.getDropPoint(e, n, dd);
34945         var targetNode = n.node;
34946         targetNode.ui.startDrop();
34947         if(!this.isValidDropPoint(n, point, dd, e, data)){
34948             targetNode.ui.endDrop();
34949             return false;
34950         }
34951         // first try to find the drop node
34952         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34953         var dropEvent = {
34954             tree : this.tree,
34955             target: targetNode,
34956             data: data,
34957             point: point,
34958             source: dd,
34959             rawEvent: e,
34960             dropNode: dropNode,
34961             cancel: !dropNode   
34962         };
34963         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34964         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34965             targetNode.ui.endDrop();
34966             return false;
34967         }
34968         // allow target changing
34969         targetNode = dropEvent.target;
34970         if(point == "append" && !targetNode.isExpanded()){
34971             targetNode.expand(false, null, function(){
34972                 this.completeDrop(dropEvent);
34973             }.createDelegate(this));
34974         }else{
34975             this.completeDrop(dropEvent);
34976         }
34977         return true;
34978     },
34979     
34980     completeDrop : function(de){
34981         var ns = de.dropNode, p = de.point, t = de.target;
34982         if(!(ns instanceof Array)){
34983             ns = [ns];
34984         }
34985         var n;
34986         for(var i = 0, len = ns.length; i < len; i++){
34987             n = ns[i];
34988             if(p == "above"){
34989                 t.parentNode.insertBefore(n, t);
34990             }else if(p == "below"){
34991                 t.parentNode.insertBefore(n, t.nextSibling);
34992             }else{
34993                 t.appendChild(n);
34994             }
34995         }
34996         n.ui.focus();
34997         if(this.tree.hlDrop){
34998             n.ui.highlight();
34999         }
35000         t.ui.endDrop();
35001         this.tree.fireEvent("nodedrop", de);
35002     },
35003     
35004     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35005         if(this.tree.hlDrop){
35006             dropNode.ui.focus();
35007             dropNode.ui.highlight();
35008         }
35009         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35010     },
35011     
35012     getTree : function(){
35013         return this.tree;
35014     },
35015     
35016     removeDropIndicators : function(n){
35017         if(n && n.ddel){
35018             var el = n.ddel;
35019             Roo.fly(el).removeClass([
35020                     "x-tree-drag-insert-above",
35021                     "x-tree-drag-insert-below",
35022                     "x-tree-drag-append"]);
35023             this.lastInsertClass = "_noclass";
35024         }
35025     },
35026     
35027     beforeDragDrop : function(target, e, id){
35028         this.cancelExpand();
35029         return true;
35030     },
35031     
35032     afterRepair : function(data){
35033         if(data && Roo.enableFx){
35034             data.node.ui.highlight();
35035         }
35036         this.hideProxy();
35037     } 
35038     
35039 });
35040
35041 }
35042 /*
35043  * Based on:
35044  * Ext JS Library 1.1.1
35045  * Copyright(c) 2006-2007, Ext JS, LLC.
35046  *
35047  * Originally Released Under LGPL - original licence link has changed is not relivant.
35048  *
35049  * Fork - LGPL
35050  * <script type="text/javascript">
35051  */
35052  
35053
35054 if(Roo.dd.DragZone){
35055 Roo.tree.TreeDragZone = function(tree, config){
35056     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35057     this.tree = tree;
35058 };
35059
35060 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35061     ddGroup : "TreeDD",
35062    
35063     onBeforeDrag : function(data, e){
35064         var n = data.node;
35065         return n && n.draggable && !n.disabled;
35066     },
35067      
35068     
35069     onInitDrag : function(e){
35070         var data = this.dragData;
35071         this.tree.getSelectionModel().select(data.node);
35072         this.proxy.update("");
35073         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35074         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35075     },
35076     
35077     getRepairXY : function(e, data){
35078         return data.node.ui.getDDRepairXY();
35079     },
35080     
35081     onEndDrag : function(data, e){
35082         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35083         
35084         
35085     },
35086     
35087     onValidDrop : function(dd, e, id){
35088         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35089         this.hideProxy();
35090     },
35091     
35092     beforeInvalidDrop : function(e, id){
35093         // this scrolls the original position back into view
35094         var sm = this.tree.getSelectionModel();
35095         sm.clearSelections();
35096         sm.select(this.dragData.node);
35097     }
35098 });
35099 }/*
35100  * Based on:
35101  * Ext JS Library 1.1.1
35102  * Copyright(c) 2006-2007, Ext JS, LLC.
35103  *
35104  * Originally Released Under LGPL - original licence link has changed is not relivant.
35105  *
35106  * Fork - LGPL
35107  * <script type="text/javascript">
35108  */
35109 /**
35110  * @class Roo.tree.TreeEditor
35111  * @extends Roo.Editor
35112  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35113  * as the editor field.
35114  * @constructor
35115  * @param {Object} config (used to be the tree panel.)
35116  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35117  * 
35118  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35119  * @cfg {Roo.form.TextField|Object} field The field configuration
35120  *
35121  * 
35122  */
35123 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35124     var tree = config;
35125     var field;
35126     if (oldconfig) { // old style..
35127         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35128     } else {
35129         // new style..
35130         tree = config.tree;
35131         config.field = config.field  || {};
35132         config.field.xtype = 'TextField';
35133         field = Roo.factory(config.field, Roo.form);
35134     }
35135     config = config || {};
35136     
35137     
35138     this.addEvents({
35139         /**
35140          * @event beforenodeedit
35141          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35142          * false from the handler of this event.
35143          * @param {Editor} this
35144          * @param {Roo.tree.Node} node 
35145          */
35146         "beforenodeedit" : true
35147     });
35148     
35149     //Roo.log(config);
35150     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35151
35152     this.tree = tree;
35153
35154     tree.on('beforeclick', this.beforeNodeClick, this);
35155     tree.getTreeEl().on('mousedown', this.hide, this);
35156     this.on('complete', this.updateNode, this);
35157     this.on('beforestartedit', this.fitToTree, this);
35158     this.on('startedit', this.bindScroll, this, {delay:10});
35159     this.on('specialkey', this.onSpecialKey, this);
35160 };
35161
35162 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35163     /**
35164      * @cfg {String} alignment
35165      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35166      */
35167     alignment: "l-l",
35168     // inherit
35169     autoSize: false,
35170     /**
35171      * @cfg {Boolean} hideEl
35172      * True to hide the bound element while the editor is displayed (defaults to false)
35173      */
35174     hideEl : false,
35175     /**
35176      * @cfg {String} cls
35177      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35178      */
35179     cls: "x-small-editor x-tree-editor",
35180     /**
35181      * @cfg {Boolean} shim
35182      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35183      */
35184     shim:false,
35185     // inherit
35186     shadow:"frame",
35187     /**
35188      * @cfg {Number} maxWidth
35189      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35190      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35191      * scroll and client offsets into account prior to each edit.
35192      */
35193     maxWidth: 250,
35194
35195     editDelay : 350,
35196
35197     // private
35198     fitToTree : function(ed, el){
35199         var td = this.tree.getTreeEl().dom, nd = el.dom;
35200         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35201             td.scrollLeft = nd.offsetLeft;
35202         }
35203         var w = Math.min(
35204                 this.maxWidth,
35205                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35206         this.setSize(w, '');
35207         
35208         return this.fireEvent('beforenodeedit', this, this.editNode);
35209         
35210     },
35211
35212     // private
35213     triggerEdit : function(node){
35214         this.completeEdit();
35215         this.editNode = node;
35216         this.startEdit(node.ui.textNode, node.text);
35217     },
35218
35219     // private
35220     bindScroll : function(){
35221         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35222     },
35223
35224     // private
35225     beforeNodeClick : function(node, e){
35226         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35227         this.lastClick = new Date();
35228         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35229             e.stopEvent();
35230             this.triggerEdit(node);
35231             return false;
35232         }
35233         return true;
35234     },
35235
35236     // private
35237     updateNode : function(ed, value){
35238         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35239         this.editNode.setText(value);
35240     },
35241
35242     // private
35243     onHide : function(){
35244         Roo.tree.TreeEditor.superclass.onHide.call(this);
35245         if(this.editNode){
35246             this.editNode.ui.focus();
35247         }
35248     },
35249
35250     // private
35251     onSpecialKey : function(field, e){
35252         var k = e.getKey();
35253         if(k == e.ESC){
35254             e.stopEvent();
35255             this.cancelEdit();
35256         }else if(k == e.ENTER && !e.hasModifier()){
35257             e.stopEvent();
35258             this.completeEdit();
35259         }
35260     }
35261 });//<Script type="text/javascript">
35262 /*
35263  * Based on:
35264  * Ext JS Library 1.1.1
35265  * Copyright(c) 2006-2007, Ext JS, LLC.
35266  *
35267  * Originally Released Under LGPL - original licence link has changed is not relivant.
35268  *
35269  * Fork - LGPL
35270  * <script type="text/javascript">
35271  */
35272  
35273 /**
35274  * Not documented??? - probably should be...
35275  */
35276
35277 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35278     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35279     
35280     renderElements : function(n, a, targetNode, bulkRender){
35281         //consel.log("renderElements?");
35282         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35283
35284         var t = n.getOwnerTree();
35285         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35286         
35287         var cols = t.columns;
35288         var bw = t.borderWidth;
35289         var c = cols[0];
35290         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35291          var cb = typeof a.checked == "boolean";
35292         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35293         var colcls = 'x-t-' + tid + '-c0';
35294         var buf = [
35295             '<li class="x-tree-node">',
35296             
35297                 
35298                 '<div class="x-tree-node-el ', a.cls,'">',
35299                     // extran...
35300                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35301                 
35302                 
35303                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35304                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35305                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35306                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35307                            (a.iconCls ? ' '+a.iconCls : ''),
35308                            '" unselectable="on" />',
35309                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35310                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35311                              
35312                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35313                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35314                             '<span unselectable="on" qtip="' + tx + '">',
35315                              tx,
35316                              '</span></a>' ,
35317                     '</div>',
35318                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35319                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35320                  ];
35321         for(var i = 1, len = cols.length; i < len; i++){
35322             c = cols[i];
35323             colcls = 'x-t-' + tid + '-c' +i;
35324             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35325             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35326                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35327                       "</div>");
35328          }
35329          
35330          buf.push(
35331             '</a>',
35332             '<div class="x-clear"></div></div>',
35333             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35334             "</li>");
35335         
35336         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35337             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35338                                 n.nextSibling.ui.getEl(), buf.join(""));
35339         }else{
35340             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35341         }
35342         var el = this.wrap.firstChild;
35343         this.elRow = el;
35344         this.elNode = el.firstChild;
35345         this.ranchor = el.childNodes[1];
35346         this.ctNode = this.wrap.childNodes[1];
35347         var cs = el.firstChild.childNodes;
35348         this.indentNode = cs[0];
35349         this.ecNode = cs[1];
35350         this.iconNode = cs[2];
35351         var index = 3;
35352         if(cb){
35353             this.checkbox = cs[3];
35354             index++;
35355         }
35356         this.anchor = cs[index];
35357         
35358         this.textNode = cs[index].firstChild;
35359         
35360         //el.on("click", this.onClick, this);
35361         //el.on("dblclick", this.onDblClick, this);
35362         
35363         
35364        // console.log(this);
35365     },
35366     initEvents : function(){
35367         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35368         
35369             
35370         var a = this.ranchor;
35371
35372         var el = Roo.get(a);
35373
35374         if(Roo.isOpera){ // opera render bug ignores the CSS
35375             el.setStyle("text-decoration", "none");
35376         }
35377
35378         el.on("click", this.onClick, this);
35379         el.on("dblclick", this.onDblClick, this);
35380         el.on("contextmenu", this.onContextMenu, this);
35381         
35382     },
35383     
35384     /*onSelectedChange : function(state){
35385         if(state){
35386             this.focus();
35387             this.addClass("x-tree-selected");
35388         }else{
35389             //this.blur();
35390             this.removeClass("x-tree-selected");
35391         }
35392     },*/
35393     addClass : function(cls){
35394         if(this.elRow){
35395             Roo.fly(this.elRow).addClass(cls);
35396         }
35397         
35398     },
35399     
35400     
35401     removeClass : function(cls){
35402         if(this.elRow){
35403             Roo.fly(this.elRow).removeClass(cls);
35404         }
35405     }
35406
35407     
35408     
35409 });//<Script type="text/javascript">
35410
35411 /*
35412  * Based on:
35413  * Ext JS Library 1.1.1
35414  * Copyright(c) 2006-2007, Ext JS, LLC.
35415  *
35416  * Originally Released Under LGPL - original licence link has changed is not relivant.
35417  *
35418  * Fork - LGPL
35419  * <script type="text/javascript">
35420  */
35421  
35422
35423 /**
35424  * @class Roo.tree.ColumnTree
35425  * @extends Roo.data.TreePanel
35426  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35427  * @cfg {int} borderWidth  compined right/left border allowance
35428  * @constructor
35429  * @param {String/HTMLElement/Element} el The container element
35430  * @param {Object} config
35431  */
35432 Roo.tree.ColumnTree =  function(el, config)
35433 {
35434    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35435    this.addEvents({
35436         /**
35437         * @event resize
35438         * Fire this event on a container when it resizes
35439         * @param {int} w Width
35440         * @param {int} h Height
35441         */
35442        "resize" : true
35443     });
35444     this.on('resize', this.onResize, this);
35445 };
35446
35447 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35448     //lines:false,
35449     
35450     
35451     borderWidth: Roo.isBorderBox ? 0 : 2, 
35452     headEls : false,
35453     
35454     render : function(){
35455         // add the header.....
35456        
35457         Roo.tree.ColumnTree.superclass.render.apply(this);
35458         
35459         this.el.addClass('x-column-tree');
35460         
35461         this.headers = this.el.createChild(
35462             {cls:'x-tree-headers'},this.innerCt.dom);
35463    
35464         var cols = this.columns, c;
35465         var totalWidth = 0;
35466         this.headEls = [];
35467         var  len = cols.length;
35468         for(var i = 0; i < len; i++){
35469              c = cols[i];
35470              totalWidth += c.width;
35471             this.headEls.push(this.headers.createChild({
35472                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35473                  cn: {
35474                      cls:'x-tree-hd-text',
35475                      html: c.header
35476                  },
35477                  style:'width:'+(c.width-this.borderWidth)+'px;'
35478              }));
35479         }
35480         this.headers.createChild({cls:'x-clear'});
35481         // prevent floats from wrapping when clipped
35482         this.headers.setWidth(totalWidth);
35483         //this.innerCt.setWidth(totalWidth);
35484         this.innerCt.setStyle({ overflow: 'auto' });
35485         this.onResize(this.width, this.height);
35486              
35487         
35488     },
35489     onResize : function(w,h)
35490     {
35491         this.height = h;
35492         this.width = w;
35493         // resize cols..
35494         this.innerCt.setWidth(this.width);
35495         this.innerCt.setHeight(this.height-20);
35496         
35497         // headers...
35498         var cols = this.columns, c;
35499         var totalWidth = 0;
35500         var expEl = false;
35501         var len = cols.length;
35502         for(var i = 0; i < len; i++){
35503             c = cols[i];
35504             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35505                 // it's the expander..
35506                 expEl  = this.headEls[i];
35507                 continue;
35508             }
35509             totalWidth += c.width;
35510             
35511         }
35512         if (expEl) {
35513             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35514         }
35515         this.headers.setWidth(w-20);
35516
35517         
35518         
35519         
35520     }
35521 });
35522 /*
35523  * Based on:
35524  * Ext JS Library 1.1.1
35525  * Copyright(c) 2006-2007, Ext JS, LLC.
35526  *
35527  * Originally Released Under LGPL - original licence link has changed is not relivant.
35528  *
35529  * Fork - LGPL
35530  * <script type="text/javascript">
35531  */
35532  
35533 /**
35534  * @class Roo.menu.Menu
35535  * @extends Roo.util.Observable
35536  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35537  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35538  * @constructor
35539  * Creates a new Menu
35540  * @param {Object} config Configuration options
35541  */
35542 Roo.menu.Menu = function(config){
35543     Roo.apply(this, config);
35544     this.id = this.id || Roo.id();
35545     this.addEvents({
35546         /**
35547          * @event beforeshow
35548          * Fires before this menu is displayed
35549          * @param {Roo.menu.Menu} this
35550          */
35551         beforeshow : true,
35552         /**
35553          * @event beforehide
35554          * Fires before this menu is hidden
35555          * @param {Roo.menu.Menu} this
35556          */
35557         beforehide : true,
35558         /**
35559          * @event show
35560          * Fires after this menu is displayed
35561          * @param {Roo.menu.Menu} this
35562          */
35563         show : true,
35564         /**
35565          * @event hide
35566          * Fires after this menu is hidden
35567          * @param {Roo.menu.Menu} this
35568          */
35569         hide : true,
35570         /**
35571          * @event click
35572          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35573          * @param {Roo.menu.Menu} this
35574          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35575          * @param {Roo.EventObject} e
35576          */
35577         click : true,
35578         /**
35579          * @event mouseover
35580          * Fires when the mouse is hovering over this menu
35581          * @param {Roo.menu.Menu} this
35582          * @param {Roo.EventObject} e
35583          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35584          */
35585         mouseover : true,
35586         /**
35587          * @event mouseout
35588          * Fires when the mouse exits this menu
35589          * @param {Roo.menu.Menu} this
35590          * @param {Roo.EventObject} e
35591          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35592          */
35593         mouseout : true,
35594         /**
35595          * @event itemclick
35596          * Fires when a menu item contained in this menu is clicked
35597          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35598          * @param {Roo.EventObject} e
35599          */
35600         itemclick: true
35601     });
35602     if (this.registerMenu) {
35603         Roo.menu.MenuMgr.register(this);
35604     }
35605     
35606     var mis = this.items;
35607     this.items = new Roo.util.MixedCollection();
35608     if(mis){
35609         this.add.apply(this, mis);
35610     }
35611 };
35612
35613 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35614     /**
35615      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35616      */
35617     minWidth : 120,
35618     /**
35619      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35620      * for bottom-right shadow (defaults to "sides")
35621      */
35622     shadow : "sides",
35623     /**
35624      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35625      * this menu (defaults to "tl-tr?")
35626      */
35627     subMenuAlign : "tl-tr?",
35628     /**
35629      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35630      * relative to its element of origin (defaults to "tl-bl?")
35631      */
35632     defaultAlign : "tl-bl?",
35633     /**
35634      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35635      */
35636     allowOtherMenus : false,
35637     /**
35638      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35639      */
35640     registerMenu : true,
35641
35642     hidden:true,
35643
35644     // private
35645     render : function(){
35646         if(this.el){
35647             return;
35648         }
35649         var el = this.el = new Roo.Layer({
35650             cls: "x-menu",
35651             shadow:this.shadow,
35652             constrain: false,
35653             parentEl: this.parentEl || document.body,
35654             zindex:15000
35655         });
35656
35657         this.keyNav = new Roo.menu.MenuNav(this);
35658
35659         if(this.plain){
35660             el.addClass("x-menu-plain");
35661         }
35662         if(this.cls){
35663             el.addClass(this.cls);
35664         }
35665         // generic focus element
35666         this.focusEl = el.createChild({
35667             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35668         });
35669         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35670         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35671         
35672         ul.on("mouseover", this.onMouseOver, this);
35673         ul.on("mouseout", this.onMouseOut, this);
35674         this.items.each(function(item){
35675             if (item.hidden) {
35676                 return;
35677             }
35678             
35679             var li = document.createElement("li");
35680             li.className = "x-menu-list-item";
35681             ul.dom.appendChild(li);
35682             item.render(li, this);
35683         }, this);
35684         this.ul = ul;
35685         this.autoWidth();
35686     },
35687
35688     // private
35689     autoWidth : function(){
35690         var el = this.el, ul = this.ul;
35691         if(!el){
35692             return;
35693         }
35694         var w = this.width;
35695         if(w){
35696             el.setWidth(w);
35697         }else if(Roo.isIE){
35698             el.setWidth(this.minWidth);
35699             var t = el.dom.offsetWidth; // force recalc
35700             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35701         }
35702     },
35703
35704     // private
35705     delayAutoWidth : function(){
35706         if(this.rendered){
35707             if(!this.awTask){
35708                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35709             }
35710             this.awTask.delay(20);
35711         }
35712     },
35713
35714     // private
35715     findTargetItem : function(e){
35716         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35717         if(t && t.menuItemId){
35718             return this.items.get(t.menuItemId);
35719         }
35720     },
35721
35722     // private
35723     onClick : function(e){
35724         Roo.log("menu.onClick");
35725         var t = this.findTargetItem(e);
35726         if(!t){
35727             return;
35728         }
35729         Roo.log(e);
35730         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35731             if(t == this.activeItem && t.shouldDeactivate(e)){
35732                 this.activeItem.deactivate();
35733                 delete this.activeItem;
35734                 return;
35735             }
35736             if(t.canActivate){
35737                 this.setActiveItem(t, true);
35738             }
35739             return;
35740             
35741             
35742         }
35743         
35744         t.onClick(e);
35745         this.fireEvent("click", this, t, e);
35746     },
35747
35748     // private
35749     setActiveItem : function(item, autoExpand){
35750         if(item != this.activeItem){
35751             if(this.activeItem){
35752                 this.activeItem.deactivate();
35753             }
35754             this.activeItem = item;
35755             item.activate(autoExpand);
35756         }else if(autoExpand){
35757             item.expandMenu();
35758         }
35759     },
35760
35761     // private
35762     tryActivate : function(start, step){
35763         var items = this.items;
35764         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35765             var item = items.get(i);
35766             if(!item.disabled && item.canActivate){
35767                 this.setActiveItem(item, false);
35768                 return item;
35769             }
35770         }
35771         return false;
35772     },
35773
35774     // private
35775     onMouseOver : function(e){
35776         var t;
35777         if(t = this.findTargetItem(e)){
35778             if(t.canActivate && !t.disabled){
35779                 this.setActiveItem(t, true);
35780             }
35781         }
35782         this.fireEvent("mouseover", this, e, t);
35783     },
35784
35785     // private
35786     onMouseOut : function(e){
35787         var t;
35788         if(t = this.findTargetItem(e)){
35789             if(t == this.activeItem && t.shouldDeactivate(e)){
35790                 this.activeItem.deactivate();
35791                 delete this.activeItem;
35792             }
35793         }
35794         this.fireEvent("mouseout", this, e, t);
35795     },
35796
35797     /**
35798      * Read-only.  Returns true if the menu is currently displayed, else false.
35799      * @type Boolean
35800      */
35801     isVisible : function(){
35802         return this.el && !this.hidden;
35803     },
35804
35805     /**
35806      * Displays this menu relative to another element
35807      * @param {String/HTMLElement/Roo.Element} element The element to align to
35808      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35809      * the element (defaults to this.defaultAlign)
35810      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35811      */
35812     show : function(el, pos, parentMenu){
35813         this.parentMenu = parentMenu;
35814         if(!this.el){
35815             this.render();
35816         }
35817         this.fireEvent("beforeshow", this);
35818         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35819     },
35820
35821     /**
35822      * Displays this menu at a specific xy position
35823      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35824      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35825      */
35826     showAt : function(xy, parentMenu, /* private: */_e){
35827         this.parentMenu = parentMenu;
35828         if(!this.el){
35829             this.render();
35830         }
35831         if(_e !== false){
35832             this.fireEvent("beforeshow", this);
35833             xy = this.el.adjustForConstraints(xy);
35834         }
35835         this.el.setXY(xy);
35836         this.el.show();
35837         this.hidden = false;
35838         this.focus();
35839         this.fireEvent("show", this);
35840     },
35841
35842     focus : function(){
35843         if(!this.hidden){
35844             this.doFocus.defer(50, this);
35845         }
35846     },
35847
35848     doFocus : function(){
35849         if(!this.hidden){
35850             this.focusEl.focus();
35851         }
35852     },
35853
35854     /**
35855      * Hides this menu and optionally all parent menus
35856      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35857      */
35858     hide : function(deep){
35859         if(this.el && this.isVisible()){
35860             this.fireEvent("beforehide", this);
35861             if(this.activeItem){
35862                 this.activeItem.deactivate();
35863                 this.activeItem = null;
35864             }
35865             this.el.hide();
35866             this.hidden = true;
35867             this.fireEvent("hide", this);
35868         }
35869         if(deep === true && this.parentMenu){
35870             this.parentMenu.hide(true);
35871         }
35872     },
35873
35874     /**
35875      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35876      * Any of the following are valid:
35877      * <ul>
35878      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35879      * <li>An HTMLElement object which will be converted to a menu item</li>
35880      * <li>A menu item config object that will be created as a new menu item</li>
35881      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35882      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35883      * </ul>
35884      * Usage:
35885      * <pre><code>
35886 // Create the menu
35887 var menu = new Roo.menu.Menu();
35888
35889 // Create a menu item to add by reference
35890 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35891
35892 // Add a bunch of items at once using different methods.
35893 // Only the last item added will be returned.
35894 var item = menu.add(
35895     menuItem,                // add existing item by ref
35896     'Dynamic Item',          // new TextItem
35897     '-',                     // new separator
35898     { text: 'Config Item' }  // new item by config
35899 );
35900 </code></pre>
35901      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35902      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35903      */
35904     add : function(){
35905         var a = arguments, l = a.length, item;
35906         for(var i = 0; i < l; i++){
35907             var el = a[i];
35908             if ((typeof(el) == "object") && el.xtype && el.xns) {
35909                 el = Roo.factory(el, Roo.menu);
35910             }
35911             
35912             if(el.render){ // some kind of Item
35913                 item = this.addItem(el);
35914             }else if(typeof el == "string"){ // string
35915                 if(el == "separator" || el == "-"){
35916                     item = this.addSeparator();
35917                 }else{
35918                     item = this.addText(el);
35919                 }
35920             }else if(el.tagName || el.el){ // element
35921                 item = this.addElement(el);
35922             }else if(typeof el == "object"){ // must be menu item config?
35923                 item = this.addMenuItem(el);
35924             }
35925         }
35926         return item;
35927     },
35928
35929     /**
35930      * Returns this menu's underlying {@link Roo.Element} object
35931      * @return {Roo.Element} The element
35932      */
35933     getEl : function(){
35934         if(!this.el){
35935             this.render();
35936         }
35937         return this.el;
35938     },
35939
35940     /**
35941      * Adds a separator bar to the menu
35942      * @return {Roo.menu.Item} The menu item that was added
35943      */
35944     addSeparator : function(){
35945         return this.addItem(new Roo.menu.Separator());
35946     },
35947
35948     /**
35949      * Adds an {@link Roo.Element} object to the menu
35950      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35951      * @return {Roo.menu.Item} The menu item that was added
35952      */
35953     addElement : function(el){
35954         return this.addItem(new Roo.menu.BaseItem(el));
35955     },
35956
35957     /**
35958      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35959      * @param {Roo.menu.Item} item The menu item to add
35960      * @return {Roo.menu.Item} The menu item that was added
35961      */
35962     addItem : function(item){
35963         this.items.add(item);
35964         if(this.ul){
35965             var li = document.createElement("li");
35966             li.className = "x-menu-list-item";
35967             this.ul.dom.appendChild(li);
35968             item.render(li, this);
35969             this.delayAutoWidth();
35970         }
35971         return item;
35972     },
35973
35974     /**
35975      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35976      * @param {Object} config A MenuItem config object
35977      * @return {Roo.menu.Item} The menu item that was added
35978      */
35979     addMenuItem : function(config){
35980         if(!(config instanceof Roo.menu.Item)){
35981             if(typeof config.checked == "boolean"){ // must be check menu item config?
35982                 config = new Roo.menu.CheckItem(config);
35983             }else{
35984                 config = new Roo.menu.Item(config);
35985             }
35986         }
35987         return this.addItem(config);
35988     },
35989
35990     /**
35991      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35992      * @param {String} text The text to display in the menu item
35993      * @return {Roo.menu.Item} The menu item that was added
35994      */
35995     addText : function(text){
35996         return this.addItem(new Roo.menu.TextItem({ text : text }));
35997     },
35998
35999     /**
36000      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36001      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36002      * @param {Roo.menu.Item} item The menu item to add
36003      * @return {Roo.menu.Item} The menu item that was added
36004      */
36005     insert : function(index, item){
36006         this.items.insert(index, item);
36007         if(this.ul){
36008             var li = document.createElement("li");
36009             li.className = "x-menu-list-item";
36010             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36011             item.render(li, this);
36012             this.delayAutoWidth();
36013         }
36014         return item;
36015     },
36016
36017     /**
36018      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36019      * @param {Roo.menu.Item} item The menu item to remove
36020      */
36021     remove : function(item){
36022         this.items.removeKey(item.id);
36023         item.destroy();
36024     },
36025
36026     /**
36027      * Removes and destroys all items in the menu
36028      */
36029     removeAll : function(){
36030         var f;
36031         while(f = this.items.first()){
36032             this.remove(f);
36033         }
36034     }
36035 });
36036
36037 // MenuNav is a private utility class used internally by the Menu
36038 Roo.menu.MenuNav = function(menu){
36039     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36040     this.scope = this.menu = menu;
36041 };
36042
36043 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36044     doRelay : function(e, h){
36045         var k = e.getKey();
36046         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36047             this.menu.tryActivate(0, 1);
36048             return false;
36049         }
36050         return h.call(this.scope || this, e, this.menu);
36051     },
36052
36053     up : function(e, m){
36054         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36055             m.tryActivate(m.items.length-1, -1);
36056         }
36057     },
36058
36059     down : function(e, m){
36060         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36061             m.tryActivate(0, 1);
36062         }
36063     },
36064
36065     right : function(e, m){
36066         if(m.activeItem){
36067             m.activeItem.expandMenu(true);
36068         }
36069     },
36070
36071     left : function(e, m){
36072         m.hide();
36073         if(m.parentMenu && m.parentMenu.activeItem){
36074             m.parentMenu.activeItem.activate();
36075         }
36076     },
36077
36078     enter : function(e, m){
36079         if(m.activeItem){
36080             e.stopPropagation();
36081             m.activeItem.onClick(e);
36082             m.fireEvent("click", this, m.activeItem);
36083             return true;
36084         }
36085     }
36086 });/*
36087  * Based on:
36088  * Ext JS Library 1.1.1
36089  * Copyright(c) 2006-2007, Ext JS, LLC.
36090  *
36091  * Originally Released Under LGPL - original licence link has changed is not relivant.
36092  *
36093  * Fork - LGPL
36094  * <script type="text/javascript">
36095  */
36096  
36097 /**
36098  * @class Roo.menu.MenuMgr
36099  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36100  * @singleton
36101  */
36102 Roo.menu.MenuMgr = function(){
36103    var menus, active, groups = {}, attached = false, lastShow = new Date();
36104
36105    // private - called when first menu is created
36106    function init(){
36107        menus = {};
36108        active = new Roo.util.MixedCollection();
36109        Roo.get(document).addKeyListener(27, function(){
36110            if(active.length > 0){
36111                hideAll();
36112            }
36113        });
36114    }
36115
36116    // private
36117    function hideAll(){
36118        if(active && active.length > 0){
36119            var c = active.clone();
36120            c.each(function(m){
36121                m.hide();
36122            });
36123        }
36124    }
36125
36126    // private
36127    function onHide(m){
36128        active.remove(m);
36129        if(active.length < 1){
36130            Roo.get(document).un("mousedown", onMouseDown);
36131            attached = false;
36132        }
36133    }
36134
36135    // private
36136    function onShow(m){
36137        var last = active.last();
36138        lastShow = new Date();
36139        active.add(m);
36140        if(!attached){
36141            Roo.get(document).on("mousedown", onMouseDown);
36142            attached = true;
36143        }
36144        if(m.parentMenu){
36145           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36146           m.parentMenu.activeChild = m;
36147        }else if(last && last.isVisible()){
36148           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36149        }
36150    }
36151
36152    // private
36153    function onBeforeHide(m){
36154        if(m.activeChild){
36155            m.activeChild.hide();
36156        }
36157        if(m.autoHideTimer){
36158            clearTimeout(m.autoHideTimer);
36159            delete m.autoHideTimer;
36160        }
36161    }
36162
36163    // private
36164    function onBeforeShow(m){
36165        var pm = m.parentMenu;
36166        if(!pm && !m.allowOtherMenus){
36167            hideAll();
36168        }else if(pm && pm.activeChild && active != m){
36169            pm.activeChild.hide();
36170        }
36171    }
36172
36173    // private
36174    function onMouseDown(e){
36175        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36176            hideAll();
36177        }
36178    }
36179
36180    // private
36181    function onBeforeCheck(mi, state){
36182        if(state){
36183            var g = groups[mi.group];
36184            for(var i = 0, l = g.length; i < l; i++){
36185                if(g[i] != mi){
36186                    g[i].setChecked(false);
36187                }
36188            }
36189        }
36190    }
36191
36192    return {
36193
36194        /**
36195         * Hides all menus that are currently visible
36196         */
36197        hideAll : function(){
36198             hideAll();  
36199        },
36200
36201        // private
36202        register : function(menu){
36203            if(!menus){
36204                init();
36205            }
36206            menus[menu.id] = menu;
36207            menu.on("beforehide", onBeforeHide);
36208            menu.on("hide", onHide);
36209            menu.on("beforeshow", onBeforeShow);
36210            menu.on("show", onShow);
36211            var g = menu.group;
36212            if(g && menu.events["checkchange"]){
36213                if(!groups[g]){
36214                    groups[g] = [];
36215                }
36216                groups[g].push(menu);
36217                menu.on("checkchange", onCheck);
36218            }
36219        },
36220
36221         /**
36222          * Returns a {@link Roo.menu.Menu} object
36223          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36224          * be used to generate and return a new Menu instance.
36225          */
36226        get : function(menu){
36227            if(typeof menu == "string"){ // menu id
36228                return menus[menu];
36229            }else if(menu.events){  // menu instance
36230                return menu;
36231            }else if(typeof menu.length == 'number'){ // array of menu items?
36232                return new Roo.menu.Menu({items:menu});
36233            }else{ // otherwise, must be a config
36234                return new Roo.menu.Menu(menu);
36235            }
36236        },
36237
36238        // private
36239        unregister : function(menu){
36240            delete menus[menu.id];
36241            menu.un("beforehide", onBeforeHide);
36242            menu.un("hide", onHide);
36243            menu.un("beforeshow", onBeforeShow);
36244            menu.un("show", onShow);
36245            var g = menu.group;
36246            if(g && menu.events["checkchange"]){
36247                groups[g].remove(menu);
36248                menu.un("checkchange", onCheck);
36249            }
36250        },
36251
36252        // private
36253        registerCheckable : function(menuItem){
36254            var g = menuItem.group;
36255            if(g){
36256                if(!groups[g]){
36257                    groups[g] = [];
36258                }
36259                groups[g].push(menuItem);
36260                menuItem.on("beforecheckchange", onBeforeCheck);
36261            }
36262        },
36263
36264        // private
36265        unregisterCheckable : function(menuItem){
36266            var g = menuItem.group;
36267            if(g){
36268                groups[g].remove(menuItem);
36269                menuItem.un("beforecheckchange", onBeforeCheck);
36270            }
36271        }
36272    };
36273 }();/*
36274  * Based on:
36275  * Ext JS Library 1.1.1
36276  * Copyright(c) 2006-2007, Ext JS, LLC.
36277  *
36278  * Originally Released Under LGPL - original licence link has changed is not relivant.
36279  *
36280  * Fork - LGPL
36281  * <script type="text/javascript">
36282  */
36283  
36284
36285 /**
36286  * @class Roo.menu.BaseItem
36287  * @extends Roo.Component
36288  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36289  * management and base configuration options shared by all menu components.
36290  * @constructor
36291  * Creates a new BaseItem
36292  * @param {Object} config Configuration options
36293  */
36294 Roo.menu.BaseItem = function(config){
36295     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36296
36297     this.addEvents({
36298         /**
36299          * @event click
36300          * Fires when this item is clicked
36301          * @param {Roo.menu.BaseItem} this
36302          * @param {Roo.EventObject} e
36303          */
36304         click: true,
36305         /**
36306          * @event activate
36307          * Fires when this item is activated
36308          * @param {Roo.menu.BaseItem} this
36309          */
36310         activate : true,
36311         /**
36312          * @event deactivate
36313          * Fires when this item is deactivated
36314          * @param {Roo.menu.BaseItem} this
36315          */
36316         deactivate : true
36317     });
36318
36319     if(this.handler){
36320         this.on("click", this.handler, this.scope, true);
36321     }
36322 };
36323
36324 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36325     /**
36326      * @cfg {Function} handler
36327      * A function that will handle the click event of this menu item (defaults to undefined)
36328      */
36329     /**
36330      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36331      */
36332     canActivate : false,
36333     
36334      /**
36335      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36336      */
36337     hidden: false,
36338     
36339     /**
36340      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36341      */
36342     activeClass : "x-menu-item-active",
36343     /**
36344      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36345      */
36346     hideOnClick : true,
36347     /**
36348      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36349      */
36350     hideDelay : 100,
36351
36352     // private
36353     ctype: "Roo.menu.BaseItem",
36354
36355     // private
36356     actionMode : "container",
36357
36358     // private
36359     render : function(container, parentMenu){
36360         this.parentMenu = parentMenu;
36361         Roo.menu.BaseItem.superclass.render.call(this, container);
36362         this.container.menuItemId = this.id;
36363     },
36364
36365     // private
36366     onRender : function(container, position){
36367         this.el = Roo.get(this.el);
36368         container.dom.appendChild(this.el.dom);
36369     },
36370
36371     // private
36372     onClick : function(e){
36373         if(!this.disabled && this.fireEvent("click", this, e) !== false
36374                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36375             this.handleClick(e);
36376         }else{
36377             e.stopEvent();
36378         }
36379     },
36380
36381     // private
36382     activate : function(){
36383         if(this.disabled){
36384             return false;
36385         }
36386         var li = this.container;
36387         li.addClass(this.activeClass);
36388         this.region = li.getRegion().adjust(2, 2, -2, -2);
36389         this.fireEvent("activate", this);
36390         return true;
36391     },
36392
36393     // private
36394     deactivate : function(){
36395         this.container.removeClass(this.activeClass);
36396         this.fireEvent("deactivate", this);
36397     },
36398
36399     // private
36400     shouldDeactivate : function(e){
36401         return !this.region || !this.region.contains(e.getPoint());
36402     },
36403
36404     // private
36405     handleClick : function(e){
36406         if(this.hideOnClick){
36407             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36408         }
36409     },
36410
36411     // private
36412     expandMenu : function(autoActivate){
36413         // do nothing
36414     },
36415
36416     // private
36417     hideMenu : function(){
36418         // do nothing
36419     }
36420 });/*
36421  * Based on:
36422  * Ext JS Library 1.1.1
36423  * Copyright(c) 2006-2007, Ext JS, LLC.
36424  *
36425  * Originally Released Under LGPL - original licence link has changed is not relivant.
36426  *
36427  * Fork - LGPL
36428  * <script type="text/javascript">
36429  */
36430  
36431 /**
36432  * @class Roo.menu.Adapter
36433  * @extends Roo.menu.BaseItem
36434  * 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.
36435  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36436  * @constructor
36437  * Creates a new Adapter
36438  * @param {Object} config Configuration options
36439  */
36440 Roo.menu.Adapter = function(component, config){
36441     Roo.menu.Adapter.superclass.constructor.call(this, config);
36442     this.component = component;
36443 };
36444 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36445     // private
36446     canActivate : true,
36447
36448     // private
36449     onRender : function(container, position){
36450         this.component.render(container);
36451         this.el = this.component.getEl();
36452     },
36453
36454     // private
36455     activate : function(){
36456         if(this.disabled){
36457             return false;
36458         }
36459         this.component.focus();
36460         this.fireEvent("activate", this);
36461         return true;
36462     },
36463
36464     // private
36465     deactivate : function(){
36466         this.fireEvent("deactivate", this);
36467     },
36468
36469     // private
36470     disable : function(){
36471         this.component.disable();
36472         Roo.menu.Adapter.superclass.disable.call(this);
36473     },
36474
36475     // private
36476     enable : function(){
36477         this.component.enable();
36478         Roo.menu.Adapter.superclass.enable.call(this);
36479     }
36480 });/*
36481  * Based on:
36482  * Ext JS Library 1.1.1
36483  * Copyright(c) 2006-2007, Ext JS, LLC.
36484  *
36485  * Originally Released Under LGPL - original licence link has changed is not relivant.
36486  *
36487  * Fork - LGPL
36488  * <script type="text/javascript">
36489  */
36490
36491 /**
36492  * @class Roo.menu.TextItem
36493  * @extends Roo.menu.BaseItem
36494  * Adds a static text string to a menu, usually used as either a heading or group separator.
36495  * Note: old style constructor with text is still supported.
36496  * 
36497  * @constructor
36498  * Creates a new TextItem
36499  * @param {Object} cfg Configuration
36500  */
36501 Roo.menu.TextItem = function(cfg){
36502     if (typeof(cfg) == 'string') {
36503         this.text = cfg;
36504     } else {
36505         Roo.apply(this,cfg);
36506     }
36507     
36508     Roo.menu.TextItem.superclass.constructor.call(this);
36509 };
36510
36511 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36512     /**
36513      * @cfg {Boolean} text Text to show on item.
36514      */
36515     text : '',
36516     
36517     /**
36518      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36519      */
36520     hideOnClick : false,
36521     /**
36522      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36523      */
36524     itemCls : "x-menu-text",
36525
36526     // private
36527     onRender : function(){
36528         var s = document.createElement("span");
36529         s.className = this.itemCls;
36530         s.innerHTML = this.text;
36531         this.el = s;
36532         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36533     }
36534 });/*
36535  * Based on:
36536  * Ext JS Library 1.1.1
36537  * Copyright(c) 2006-2007, Ext JS, LLC.
36538  *
36539  * Originally Released Under LGPL - original licence link has changed is not relivant.
36540  *
36541  * Fork - LGPL
36542  * <script type="text/javascript">
36543  */
36544
36545 /**
36546  * @class Roo.menu.Separator
36547  * @extends Roo.menu.BaseItem
36548  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36549  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36550  * @constructor
36551  * @param {Object} config Configuration options
36552  */
36553 Roo.menu.Separator = function(config){
36554     Roo.menu.Separator.superclass.constructor.call(this, config);
36555 };
36556
36557 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36558     /**
36559      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36560      */
36561     itemCls : "x-menu-sep",
36562     /**
36563      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36564      */
36565     hideOnClick : false,
36566
36567     // private
36568     onRender : function(li){
36569         var s = document.createElement("span");
36570         s.className = this.itemCls;
36571         s.innerHTML = "&#160;";
36572         this.el = s;
36573         li.addClass("x-menu-sep-li");
36574         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36575     }
36576 });/*
36577  * Based on:
36578  * Ext JS Library 1.1.1
36579  * Copyright(c) 2006-2007, Ext JS, LLC.
36580  *
36581  * Originally Released Under LGPL - original licence link has changed is not relivant.
36582  *
36583  * Fork - LGPL
36584  * <script type="text/javascript">
36585  */
36586 /**
36587  * @class Roo.menu.Item
36588  * @extends Roo.menu.BaseItem
36589  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36590  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36591  * activation and click handling.
36592  * @constructor
36593  * Creates a new Item
36594  * @param {Object} config Configuration options
36595  */
36596 Roo.menu.Item = function(config){
36597     Roo.menu.Item.superclass.constructor.call(this, config);
36598     if(this.menu){
36599         this.menu = Roo.menu.MenuMgr.get(this.menu);
36600     }
36601 };
36602 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36603     
36604     /**
36605      * @cfg {String} text
36606      * The text to show on the menu item.
36607      */
36608     text: '',
36609      /**
36610      * @cfg {String} HTML to render in menu
36611      * The text to show on the menu item (HTML version).
36612      */
36613     html: '',
36614     /**
36615      * @cfg {String} icon
36616      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36617      */
36618     icon: undefined,
36619     /**
36620      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36621      */
36622     itemCls : "x-menu-item",
36623     /**
36624      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36625      */
36626     canActivate : true,
36627     /**
36628      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36629      */
36630     showDelay: 200,
36631     // doc'd in BaseItem
36632     hideDelay: 200,
36633
36634     // private
36635     ctype: "Roo.menu.Item",
36636     
36637     // private
36638     onRender : function(container, position){
36639         var el = document.createElement("a");
36640         el.hideFocus = true;
36641         el.unselectable = "on";
36642         el.href = this.href || "#";
36643         if(this.hrefTarget){
36644             el.target = this.hrefTarget;
36645         }
36646         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36647         
36648         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36649         
36650         el.innerHTML = String.format(
36651                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36652                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36653         this.el = el;
36654         Roo.menu.Item.superclass.onRender.call(this, container, position);
36655     },
36656
36657     /**
36658      * Sets the text to display in this menu item
36659      * @param {String} text The text to display
36660      * @param {Boolean} isHTML true to indicate text is pure html.
36661      */
36662     setText : function(text, isHTML){
36663         if (isHTML) {
36664             this.html = text;
36665         } else {
36666             this.text = text;
36667             this.html = '';
36668         }
36669         if(this.rendered){
36670             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36671      
36672             this.el.update(String.format(
36673                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36674                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36675             this.parentMenu.autoWidth();
36676         }
36677     },
36678
36679     // private
36680     handleClick : function(e){
36681         if(!this.href){ // if no link defined, stop the event automatically
36682             e.stopEvent();
36683         }
36684         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36685     },
36686
36687     // private
36688     activate : function(autoExpand){
36689         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36690             this.focus();
36691             if(autoExpand){
36692                 this.expandMenu();
36693             }
36694         }
36695         return true;
36696     },
36697
36698     // private
36699     shouldDeactivate : function(e){
36700         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36701             if(this.menu && this.menu.isVisible()){
36702                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36703             }
36704             return true;
36705         }
36706         return false;
36707     },
36708
36709     // private
36710     deactivate : function(){
36711         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36712         this.hideMenu();
36713     },
36714
36715     // private
36716     expandMenu : function(autoActivate){
36717         if(!this.disabled && this.menu){
36718             clearTimeout(this.hideTimer);
36719             delete this.hideTimer;
36720             if(!this.menu.isVisible() && !this.showTimer){
36721                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36722             }else if (this.menu.isVisible() && autoActivate){
36723                 this.menu.tryActivate(0, 1);
36724             }
36725         }
36726     },
36727
36728     // private
36729     deferExpand : function(autoActivate){
36730         delete this.showTimer;
36731         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36732         if(autoActivate){
36733             this.menu.tryActivate(0, 1);
36734         }
36735     },
36736
36737     // private
36738     hideMenu : function(){
36739         clearTimeout(this.showTimer);
36740         delete this.showTimer;
36741         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36742             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36743         }
36744     },
36745
36746     // private
36747     deferHide : function(){
36748         delete this.hideTimer;
36749         this.menu.hide();
36750     }
36751 });/*
36752  * Based on:
36753  * Ext JS Library 1.1.1
36754  * Copyright(c) 2006-2007, Ext JS, LLC.
36755  *
36756  * Originally Released Under LGPL - original licence link has changed is not relivant.
36757  *
36758  * Fork - LGPL
36759  * <script type="text/javascript">
36760  */
36761  
36762 /**
36763  * @class Roo.menu.CheckItem
36764  * @extends Roo.menu.Item
36765  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36766  * @constructor
36767  * Creates a new CheckItem
36768  * @param {Object} config Configuration options
36769  */
36770 Roo.menu.CheckItem = function(config){
36771     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36772     this.addEvents({
36773         /**
36774          * @event beforecheckchange
36775          * Fires before the checked value is set, providing an opportunity to cancel if needed
36776          * @param {Roo.menu.CheckItem} this
36777          * @param {Boolean} checked The new checked value that will be set
36778          */
36779         "beforecheckchange" : true,
36780         /**
36781          * @event checkchange
36782          * Fires after the checked value has been set
36783          * @param {Roo.menu.CheckItem} this
36784          * @param {Boolean} checked The checked value that was set
36785          */
36786         "checkchange" : true
36787     });
36788     if(this.checkHandler){
36789         this.on('checkchange', this.checkHandler, this.scope);
36790     }
36791 };
36792 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36793     /**
36794      * @cfg {String} group
36795      * All check items with the same group name will automatically be grouped into a single-select
36796      * radio button group (defaults to '')
36797      */
36798     /**
36799      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36800      */
36801     itemCls : "x-menu-item x-menu-check-item",
36802     /**
36803      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36804      */
36805     groupClass : "x-menu-group-item",
36806
36807     /**
36808      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36809      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36810      * initialized with checked = true will be rendered as checked.
36811      */
36812     checked: false,
36813
36814     // private
36815     ctype: "Roo.menu.CheckItem",
36816
36817     // private
36818     onRender : function(c){
36819         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36820         if(this.group){
36821             this.el.addClass(this.groupClass);
36822         }
36823         Roo.menu.MenuMgr.registerCheckable(this);
36824         if(this.checked){
36825             this.checked = false;
36826             this.setChecked(true, true);
36827         }
36828     },
36829
36830     // private
36831     destroy : function(){
36832         if(this.rendered){
36833             Roo.menu.MenuMgr.unregisterCheckable(this);
36834         }
36835         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36836     },
36837
36838     /**
36839      * Set the checked state of this item
36840      * @param {Boolean} checked The new checked value
36841      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36842      */
36843     setChecked : function(state, suppressEvent){
36844         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36845             if(this.container){
36846                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36847             }
36848             this.checked = state;
36849             if(suppressEvent !== true){
36850                 this.fireEvent("checkchange", this, state);
36851             }
36852         }
36853     },
36854
36855     // private
36856     handleClick : function(e){
36857        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36858            this.setChecked(!this.checked);
36859        }
36860        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36861     }
36862 });/*
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  * @class Roo.menu.DateItem
36875  * @extends Roo.menu.Adapter
36876  * A menu item that wraps the {@link Roo.DatPicker} component.
36877  * @constructor
36878  * Creates a new DateItem
36879  * @param {Object} config Configuration options
36880  */
36881 Roo.menu.DateItem = function(config){
36882     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36883     /** The Roo.DatePicker object @type Roo.DatePicker */
36884     this.picker = this.component;
36885     this.addEvents({select: true});
36886     
36887     this.picker.on("render", function(picker){
36888         picker.getEl().swallowEvent("click");
36889         picker.container.addClass("x-menu-date-item");
36890     });
36891
36892     this.picker.on("select", this.onSelect, this);
36893 };
36894
36895 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36896     // private
36897     onSelect : function(picker, date){
36898         this.fireEvent("select", this, date, picker);
36899         Roo.menu.DateItem.superclass.handleClick.call(this);
36900     }
36901 });/*
36902  * Based on:
36903  * Ext JS Library 1.1.1
36904  * Copyright(c) 2006-2007, Ext JS, LLC.
36905  *
36906  * Originally Released Under LGPL - original licence link has changed is not relivant.
36907  *
36908  * Fork - LGPL
36909  * <script type="text/javascript">
36910  */
36911  
36912 /**
36913  * @class Roo.menu.ColorItem
36914  * @extends Roo.menu.Adapter
36915  * A menu item that wraps the {@link Roo.ColorPalette} component.
36916  * @constructor
36917  * Creates a new ColorItem
36918  * @param {Object} config Configuration options
36919  */
36920 Roo.menu.ColorItem = function(config){
36921     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36922     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36923     this.palette = this.component;
36924     this.relayEvents(this.palette, ["select"]);
36925     if(this.selectHandler){
36926         this.on('select', this.selectHandler, this.scope);
36927     }
36928 };
36929 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36930  * Based on:
36931  * Ext JS Library 1.1.1
36932  * Copyright(c) 2006-2007, Ext JS, LLC.
36933  *
36934  * Originally Released Under LGPL - original licence link has changed is not relivant.
36935  *
36936  * Fork - LGPL
36937  * <script type="text/javascript">
36938  */
36939  
36940
36941 /**
36942  * @class Roo.menu.DateMenu
36943  * @extends Roo.menu.Menu
36944  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36945  * @constructor
36946  * Creates a new DateMenu
36947  * @param {Object} config Configuration options
36948  */
36949 Roo.menu.DateMenu = function(config){
36950     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36951     this.plain = true;
36952     var di = new Roo.menu.DateItem(config);
36953     this.add(di);
36954     /**
36955      * The {@link Roo.DatePicker} instance for this DateMenu
36956      * @type DatePicker
36957      */
36958     this.picker = di.picker;
36959     /**
36960      * @event select
36961      * @param {DatePicker} picker
36962      * @param {Date} date
36963      */
36964     this.relayEvents(di, ["select"]);
36965     this.on('beforeshow', function(){
36966         if(this.picker){
36967             this.picker.hideMonthPicker(false);
36968         }
36969     }, this);
36970 };
36971 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36972     cls:'x-date-menu'
36973 });/*
36974  * Based on:
36975  * Ext JS Library 1.1.1
36976  * Copyright(c) 2006-2007, Ext JS, LLC.
36977  *
36978  * Originally Released Under LGPL - original licence link has changed is not relivant.
36979  *
36980  * Fork - LGPL
36981  * <script type="text/javascript">
36982  */
36983  
36984
36985 /**
36986  * @class Roo.menu.ColorMenu
36987  * @extends Roo.menu.Menu
36988  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36989  * @constructor
36990  * Creates a new ColorMenu
36991  * @param {Object} config Configuration options
36992  */
36993 Roo.menu.ColorMenu = function(config){
36994     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36995     this.plain = true;
36996     var ci = new Roo.menu.ColorItem(config);
36997     this.add(ci);
36998     /**
36999      * The {@link Roo.ColorPalette} instance for this ColorMenu
37000      * @type ColorPalette
37001      */
37002     this.palette = ci.palette;
37003     /**
37004      * @event select
37005      * @param {ColorPalette} palette
37006      * @param {String} color
37007      */
37008     this.relayEvents(ci, ["select"]);
37009 };
37010 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37011  * Based on:
37012  * Ext JS Library 1.1.1
37013  * Copyright(c) 2006-2007, Ext JS, LLC.
37014  *
37015  * Originally Released Under LGPL - original licence link has changed is not relivant.
37016  *
37017  * Fork - LGPL
37018  * <script type="text/javascript">
37019  */
37020  
37021 /**
37022  * @class Roo.form.Field
37023  * @extends Roo.BoxComponent
37024  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37025  * @constructor
37026  * Creates a new Field
37027  * @param {Object} config Configuration options
37028  */
37029 Roo.form.Field = function(config){
37030     Roo.form.Field.superclass.constructor.call(this, config);
37031 };
37032
37033 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37034     /**
37035      * @cfg {String} fieldLabel Label to use when rendering a form.
37036      */
37037        /**
37038      * @cfg {String} qtip Mouse over tip
37039      */
37040      
37041     /**
37042      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37043      */
37044     invalidClass : "x-form-invalid",
37045     /**
37046      * @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")
37047      */
37048     invalidText : "The value in this field is invalid",
37049     /**
37050      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37051      */
37052     focusClass : "x-form-focus",
37053     /**
37054      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37055       automatic validation (defaults to "keyup").
37056      */
37057     validationEvent : "keyup",
37058     /**
37059      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37060      */
37061     validateOnBlur : true,
37062     /**
37063      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37064      */
37065     validationDelay : 250,
37066     /**
37067      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37068      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37069      */
37070     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
37071     /**
37072      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37073      */
37074     fieldClass : "x-form-field",
37075     /**
37076      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37077      *<pre>
37078 Value         Description
37079 -----------   ----------------------------------------------------------------------
37080 qtip          Display a quick tip when the user hovers over the field
37081 title         Display a default browser title attribute popup
37082 under         Add a block div beneath the field containing the error text
37083 side          Add an error icon to the right of the field with a popup on hover
37084 [element id]  Add the error text directly to the innerHTML of the specified element
37085 </pre>
37086      */
37087     msgTarget : 'qtip',
37088     /**
37089      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37090      */
37091     msgFx : 'normal',
37092
37093     /**
37094      * @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.
37095      */
37096     readOnly : false,
37097
37098     /**
37099      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37100      */
37101     disabled : false,
37102
37103     /**
37104      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37105      */
37106     inputType : undefined,
37107     
37108     /**
37109      * @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).
37110          */
37111         tabIndex : undefined,
37112         
37113     // private
37114     isFormField : true,
37115
37116     // private
37117     hasFocus : false,
37118     /**
37119      * @property {Roo.Element} fieldEl
37120      * Element Containing the rendered Field (with label etc.)
37121      */
37122     /**
37123      * @cfg {Mixed} value A value to initialize this field with.
37124      */
37125     value : undefined,
37126
37127     /**
37128      * @cfg {String} name The field's HTML name attribute.
37129      */
37130     /**
37131      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37132      */
37133
37134         // private ??
37135         initComponent : function(){
37136         Roo.form.Field.superclass.initComponent.call(this);
37137         this.addEvents({
37138             /**
37139              * @event focus
37140              * Fires when this field receives input focus.
37141              * @param {Roo.form.Field} this
37142              */
37143             focus : true,
37144             /**
37145              * @event blur
37146              * Fires when this field loses input focus.
37147              * @param {Roo.form.Field} this
37148              */
37149             blur : true,
37150             /**
37151              * @event specialkey
37152              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37153              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37154              * @param {Roo.form.Field} this
37155              * @param {Roo.EventObject} e The event object
37156              */
37157             specialkey : true,
37158             /**
37159              * @event change
37160              * Fires just before the field blurs if the field value has changed.
37161              * @param {Roo.form.Field} this
37162              * @param {Mixed} newValue The new value
37163              * @param {Mixed} oldValue The original value
37164              */
37165             change : true,
37166             /**
37167              * @event invalid
37168              * Fires after the field has been marked as invalid.
37169              * @param {Roo.form.Field} this
37170              * @param {String} msg The validation message
37171              */
37172             invalid : true,
37173             /**
37174              * @event valid
37175              * Fires after the field has been validated with no errors.
37176              * @param {Roo.form.Field} this
37177              */
37178             valid : true,
37179              /**
37180              * @event keyup
37181              * Fires after the key up
37182              * @param {Roo.form.Field} this
37183              * @param {Roo.EventObject}  e The event Object
37184              */
37185             keyup : true
37186         });
37187     },
37188
37189     /**
37190      * Returns the name attribute of the field if available
37191      * @return {String} name The field name
37192      */
37193     getName: function(){
37194          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37195     },
37196
37197     // private
37198     onRender : function(ct, position){
37199         Roo.form.Field.superclass.onRender.call(this, ct, position);
37200         if(!this.el){
37201             var cfg = this.getAutoCreate();
37202             if(!cfg.name){
37203                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37204             }
37205             if (!cfg.name.length) {
37206                 delete cfg.name;
37207             }
37208             if(this.inputType){
37209                 cfg.type = this.inputType;
37210             }
37211             this.el = ct.createChild(cfg, position);
37212         }
37213         var type = this.el.dom.type;
37214         if(type){
37215             if(type == 'password'){
37216                 type = 'text';
37217             }
37218             this.el.addClass('x-form-'+type);
37219         }
37220         if(this.readOnly){
37221             this.el.dom.readOnly = true;
37222         }
37223         if(this.tabIndex !== undefined){
37224             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37225         }
37226
37227         this.el.addClass([this.fieldClass, this.cls]);
37228         this.initValue();
37229     },
37230
37231     /**
37232      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37233      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37234      * @return {Roo.form.Field} this
37235      */
37236     applyTo : function(target){
37237         this.allowDomMove = false;
37238         this.el = Roo.get(target);
37239         this.render(this.el.dom.parentNode);
37240         return this;
37241     },
37242
37243     // private
37244     initValue : function(){
37245         if(this.value !== undefined){
37246             this.setValue(this.value);
37247         }else if(this.el.dom.value.length > 0){
37248             this.setValue(this.el.dom.value);
37249         }
37250     },
37251
37252     /**
37253      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37254      */
37255     isDirty : function() {
37256         if(this.disabled) {
37257             return false;
37258         }
37259         return String(this.getValue()) !== String(this.originalValue);
37260     },
37261
37262     // private
37263     afterRender : function(){
37264         Roo.form.Field.superclass.afterRender.call(this);
37265         this.initEvents();
37266     },
37267
37268     // private
37269     fireKey : function(e){
37270         //Roo.log('field ' + e.getKey());
37271         if(e.isNavKeyPress()){
37272             this.fireEvent("specialkey", this, e);
37273         }
37274     },
37275
37276     /**
37277      * Resets the current field value to the originally loaded value and clears any validation messages
37278      */
37279     reset : function(){
37280         this.setValue(this.resetValue);
37281         this.clearInvalid();
37282     },
37283
37284     // private
37285     initEvents : function(){
37286         // safari killled keypress - so keydown is now used..
37287         this.el.on("keydown" , this.fireKey,  this);
37288         this.el.on("focus", this.onFocus,  this);
37289         this.el.on("blur", this.onBlur,  this);
37290         this.el.relayEvent('keyup', this);
37291
37292         // reference to original value for reset
37293         this.originalValue = this.getValue();
37294         this.resetValue =  this.getValue();
37295     },
37296
37297     // private
37298     onFocus : function(){
37299         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37300             this.el.addClass(this.focusClass);
37301         }
37302         if(!this.hasFocus){
37303             this.hasFocus = true;
37304             this.startValue = this.getValue();
37305             this.fireEvent("focus", this);
37306         }
37307     },
37308
37309     beforeBlur : Roo.emptyFn,
37310
37311     // private
37312     onBlur : function(){
37313         this.beforeBlur();
37314         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37315             this.el.removeClass(this.focusClass);
37316         }
37317         this.hasFocus = false;
37318         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37319             this.validate();
37320         }
37321         var v = this.getValue();
37322         if(String(v) !== String(this.startValue)){
37323             this.fireEvent('change', this, v, this.startValue);
37324         }
37325         this.fireEvent("blur", this);
37326     },
37327
37328     /**
37329      * Returns whether or not the field value is currently valid
37330      * @param {Boolean} preventMark True to disable marking the field invalid
37331      * @return {Boolean} True if the value is valid, else false
37332      */
37333     isValid : function(preventMark){
37334         if(this.disabled){
37335             return true;
37336         }
37337         var restore = this.preventMark;
37338         this.preventMark = preventMark === true;
37339         var v = this.validateValue(this.processValue(this.getRawValue()));
37340         this.preventMark = restore;
37341         return v;
37342     },
37343
37344     /**
37345      * Validates the field value
37346      * @return {Boolean} True if the value is valid, else false
37347      */
37348     validate : function(){
37349         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37350             this.clearInvalid();
37351             return true;
37352         }
37353         return false;
37354     },
37355
37356     processValue : function(value){
37357         return value;
37358     },
37359
37360     // private
37361     // Subclasses should provide the validation implementation by overriding this
37362     validateValue : function(value){
37363         return true;
37364     },
37365
37366     /**
37367      * Mark this field as invalid
37368      * @param {String} msg The validation message
37369      */
37370     markInvalid : function(msg){
37371         if(!this.rendered || this.preventMark){ // not rendered
37372             return;
37373         }
37374         
37375         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37376         
37377         obj.el.addClass(this.invalidClass);
37378         msg = msg || this.invalidText;
37379         switch(this.msgTarget){
37380             case 'qtip':
37381                 obj.el.dom.qtip = msg;
37382                 obj.el.dom.qclass = 'x-form-invalid-tip';
37383                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37384                     Roo.QuickTips.enable();
37385                 }
37386                 break;
37387             case 'title':
37388                 this.el.dom.title = msg;
37389                 break;
37390             case 'under':
37391                 if(!this.errorEl){
37392                     var elp = this.el.findParent('.x-form-element', 5, true);
37393                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37394                     this.errorEl.setWidth(elp.getWidth(true)-20);
37395                 }
37396                 this.errorEl.update(msg);
37397                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37398                 break;
37399             case 'side':
37400                 if(!this.errorIcon){
37401                     var elp = this.el.findParent('.x-form-element', 5, true);
37402                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37403                 }
37404                 this.alignErrorIcon();
37405                 this.errorIcon.dom.qtip = msg;
37406                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37407                 this.errorIcon.show();
37408                 this.on('resize', this.alignErrorIcon, this);
37409                 break;
37410             default:
37411                 var t = Roo.getDom(this.msgTarget);
37412                 t.innerHTML = msg;
37413                 t.style.display = this.msgDisplay;
37414                 break;
37415         }
37416         this.fireEvent('invalid', this, msg);
37417     },
37418
37419     // private
37420     alignErrorIcon : function(){
37421         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37422     },
37423
37424     /**
37425      * Clear any invalid styles/messages for this field
37426      */
37427     clearInvalid : function(){
37428         if(!this.rendered || this.preventMark){ // not rendered
37429             return;
37430         }
37431         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37432         
37433         obj.el.removeClass(this.invalidClass);
37434         switch(this.msgTarget){
37435             case 'qtip':
37436                 obj.el.dom.qtip = '';
37437                 break;
37438             case 'title':
37439                 this.el.dom.title = '';
37440                 break;
37441             case 'under':
37442                 if(this.errorEl){
37443                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37444                 }
37445                 break;
37446             case 'side':
37447                 if(this.errorIcon){
37448                     this.errorIcon.dom.qtip = '';
37449                     this.errorIcon.hide();
37450                     this.un('resize', this.alignErrorIcon, this);
37451                 }
37452                 break;
37453             default:
37454                 var t = Roo.getDom(this.msgTarget);
37455                 t.innerHTML = '';
37456                 t.style.display = 'none';
37457                 break;
37458         }
37459         this.fireEvent('valid', this);
37460     },
37461
37462     /**
37463      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37464      * @return {Mixed} value The field value
37465      */
37466     getRawValue : function(){
37467         var v = this.el.getValue();
37468         
37469         return v;
37470     },
37471
37472     /**
37473      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37474      * @return {Mixed} value The field value
37475      */
37476     getValue : function(){
37477         var v = this.el.getValue();
37478          
37479         return v;
37480     },
37481
37482     /**
37483      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37484      * @param {Mixed} value The value to set
37485      */
37486     setRawValue : function(v){
37487         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37488     },
37489
37490     /**
37491      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37492      * @param {Mixed} value The value to set
37493      */
37494     setValue : function(v){
37495         this.value = v;
37496         if(this.rendered){
37497             this.el.dom.value = (v === null || v === undefined ? '' : v);
37498              this.validate();
37499         }
37500     },
37501
37502     adjustSize : function(w, h){
37503         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37504         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37505         return s;
37506     },
37507
37508     adjustWidth : function(tag, w){
37509         tag = tag.toLowerCase();
37510         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37511             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37512                 if(tag == 'input'){
37513                     return w + 2;
37514                 }
37515                 if(tag == 'textarea'){
37516                     return w-2;
37517                 }
37518             }else if(Roo.isOpera){
37519                 if(tag == 'input'){
37520                     return w + 2;
37521                 }
37522                 if(tag == 'textarea'){
37523                     return w-2;
37524                 }
37525             }
37526         }
37527         return w;
37528     }
37529 });
37530
37531
37532 // anything other than normal should be considered experimental
37533 Roo.form.Field.msgFx = {
37534     normal : {
37535         show: function(msgEl, f){
37536             msgEl.setDisplayed('block');
37537         },
37538
37539         hide : function(msgEl, f){
37540             msgEl.setDisplayed(false).update('');
37541         }
37542     },
37543
37544     slide : {
37545         show: function(msgEl, f){
37546             msgEl.slideIn('t', {stopFx:true});
37547         },
37548
37549         hide : function(msgEl, f){
37550             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37551         }
37552     },
37553
37554     slideRight : {
37555         show: function(msgEl, f){
37556             msgEl.fixDisplay();
37557             msgEl.alignTo(f.el, 'tl-tr');
37558             msgEl.slideIn('l', {stopFx:true});
37559         },
37560
37561         hide : function(msgEl, f){
37562             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37563         }
37564     }
37565 };/*
37566  * Based on:
37567  * Ext JS Library 1.1.1
37568  * Copyright(c) 2006-2007, Ext JS, LLC.
37569  *
37570  * Originally Released Under LGPL - original licence link has changed is not relivant.
37571  *
37572  * Fork - LGPL
37573  * <script type="text/javascript">
37574  */
37575  
37576
37577 /**
37578  * @class Roo.form.TextField
37579  * @extends Roo.form.Field
37580  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37581  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37582  * @constructor
37583  * Creates a new TextField
37584  * @param {Object} config Configuration options
37585  */
37586 Roo.form.TextField = function(config){
37587     Roo.form.TextField.superclass.constructor.call(this, config);
37588     this.addEvents({
37589         /**
37590          * @event autosize
37591          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37592          * according to the default logic, but this event provides a hook for the developer to apply additional
37593          * logic at runtime to resize the field if needed.
37594              * @param {Roo.form.Field} this This text field
37595              * @param {Number} width The new field width
37596              */
37597         autosize : true
37598     });
37599 };
37600
37601 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37602     /**
37603      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37604      */
37605     grow : false,
37606     /**
37607      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37608      */
37609     growMin : 30,
37610     /**
37611      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37612      */
37613     growMax : 800,
37614     /**
37615      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37616      */
37617     vtype : null,
37618     /**
37619      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37620      */
37621     maskRe : null,
37622     /**
37623      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37624      */
37625     disableKeyFilter : false,
37626     /**
37627      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37628      */
37629     allowBlank : true,
37630     /**
37631      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37632      */
37633     minLength : 0,
37634     /**
37635      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37636      */
37637     maxLength : Number.MAX_VALUE,
37638     /**
37639      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37640      */
37641     minLengthText : "The minimum length for this field is {0}",
37642     /**
37643      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37644      */
37645     maxLengthText : "The maximum length for this field is {0}",
37646     /**
37647      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37648      */
37649     selectOnFocus : false,
37650     /**
37651      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37652      */
37653     blankText : "This field is required",
37654     /**
37655      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37656      * If available, this function will be called only after the basic validators all return true, and will be passed the
37657      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37658      */
37659     validator : null,
37660     /**
37661      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37662      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37663      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37664      */
37665     regex : null,
37666     /**
37667      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37668      */
37669     regexText : "",
37670     /**
37671      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37672      */
37673     emptyText : null,
37674    
37675
37676     // private
37677     initEvents : function()
37678     {
37679         if (this.emptyText) {
37680             this.el.attr('placeholder', this.emptyText);
37681         }
37682         
37683         Roo.form.TextField.superclass.initEvents.call(this);
37684         if(this.validationEvent == 'keyup'){
37685             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37686             this.el.on('keyup', this.filterValidation, this);
37687         }
37688         else if(this.validationEvent !== false){
37689             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37690         }
37691         
37692         if(this.selectOnFocus){
37693             this.on("focus", this.preFocus, this);
37694             
37695         }
37696         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37697             this.el.on("keypress", this.filterKeys, this);
37698         }
37699         if(this.grow){
37700             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37701             this.el.on("click", this.autoSize,  this);
37702         }
37703         if(this.el.is('input[type=password]') && Roo.isSafari){
37704             this.el.on('keydown', this.SafariOnKeyDown, this);
37705         }
37706     },
37707
37708     processValue : function(value){
37709         if(this.stripCharsRe){
37710             var newValue = value.replace(this.stripCharsRe, '');
37711             if(newValue !== value){
37712                 this.setRawValue(newValue);
37713                 return newValue;
37714             }
37715         }
37716         return value;
37717     },
37718
37719     filterValidation : function(e){
37720         if(!e.isNavKeyPress()){
37721             this.validationTask.delay(this.validationDelay);
37722         }
37723     },
37724
37725     // private
37726     onKeyUp : function(e){
37727         if(!e.isNavKeyPress()){
37728             this.autoSize();
37729         }
37730     },
37731
37732     /**
37733      * Resets the current field value to the originally-loaded value and clears any validation messages.
37734      *  
37735      */
37736     reset : function(){
37737         Roo.form.TextField.superclass.reset.call(this);
37738        
37739     },
37740
37741     
37742     // private
37743     preFocus : function(){
37744         
37745         if(this.selectOnFocus){
37746             this.el.dom.select();
37747         }
37748     },
37749
37750     
37751     // private
37752     filterKeys : function(e){
37753         var k = e.getKey();
37754         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37755             return;
37756         }
37757         var c = e.getCharCode(), cc = String.fromCharCode(c);
37758         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37759             return;
37760         }
37761         if(!this.maskRe.test(cc)){
37762             e.stopEvent();
37763         }
37764     },
37765
37766     setValue : function(v){
37767         
37768         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37769         
37770         this.autoSize();
37771     },
37772
37773     /**
37774      * Validates a value according to the field's validation rules and marks the field as invalid
37775      * if the validation fails
37776      * @param {Mixed} value The value to validate
37777      * @return {Boolean} True if the value is valid, else false
37778      */
37779     validateValue : function(value){
37780         if(value.length < 1)  { // if it's blank
37781              if(this.allowBlank){
37782                 this.clearInvalid();
37783                 return true;
37784              }else{
37785                 this.markInvalid(this.blankText);
37786                 return false;
37787              }
37788         }
37789         if(value.length < this.minLength){
37790             this.markInvalid(String.format(this.minLengthText, this.minLength));
37791             return false;
37792         }
37793         if(value.length > this.maxLength){
37794             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37795             return false;
37796         }
37797         if(this.vtype){
37798             var vt = Roo.form.VTypes;
37799             if(!vt[this.vtype](value, this)){
37800                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37801                 return false;
37802             }
37803         }
37804         if(typeof this.validator == "function"){
37805             var msg = this.validator(value);
37806             if(msg !== true){
37807                 this.markInvalid(msg);
37808                 return false;
37809             }
37810         }
37811         if(this.regex && !this.regex.test(value)){
37812             this.markInvalid(this.regexText);
37813             return false;
37814         }
37815         return true;
37816     },
37817
37818     /**
37819      * Selects text in this field
37820      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37821      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37822      */
37823     selectText : function(start, end){
37824         var v = this.getRawValue();
37825         if(v.length > 0){
37826             start = start === undefined ? 0 : start;
37827             end = end === undefined ? v.length : end;
37828             var d = this.el.dom;
37829             if(d.setSelectionRange){
37830                 d.setSelectionRange(start, end);
37831             }else if(d.createTextRange){
37832                 var range = d.createTextRange();
37833                 range.moveStart("character", start);
37834                 range.moveEnd("character", v.length-end);
37835                 range.select();
37836             }
37837         }
37838     },
37839
37840     /**
37841      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37842      * This only takes effect if grow = true, and fires the autosize event.
37843      */
37844     autoSize : function(){
37845         if(!this.grow || !this.rendered){
37846             return;
37847         }
37848         if(!this.metrics){
37849             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37850         }
37851         var el = this.el;
37852         var v = el.dom.value;
37853         var d = document.createElement('div');
37854         d.appendChild(document.createTextNode(v));
37855         v = d.innerHTML;
37856         d = null;
37857         v += "&#160;";
37858         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37859         this.el.setWidth(w);
37860         this.fireEvent("autosize", this, w);
37861     },
37862     
37863     // private
37864     SafariOnKeyDown : function(event)
37865     {
37866         // this is a workaround for a password hang bug on chrome/ webkit.
37867         
37868         var isSelectAll = false;
37869         
37870         if(this.el.dom.selectionEnd > 0){
37871             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37872         }
37873         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37874             event.preventDefault();
37875             this.setValue('');
37876             return;
37877         }
37878         
37879         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37880             
37881             event.preventDefault();
37882             // this is very hacky as keydown always get's upper case.
37883             
37884             var cc = String.fromCharCode(event.getCharCode());
37885             
37886             
37887             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37888             
37889         }
37890         
37891         
37892     }
37893 });/*
37894  * Based on:
37895  * Ext JS Library 1.1.1
37896  * Copyright(c) 2006-2007, Ext JS, LLC.
37897  *
37898  * Originally Released Under LGPL - original licence link has changed is not relivant.
37899  *
37900  * Fork - LGPL
37901  * <script type="text/javascript">
37902  */
37903  
37904 /**
37905  * @class Roo.form.Hidden
37906  * @extends Roo.form.TextField
37907  * Simple Hidden element used on forms 
37908  * 
37909  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37910  * 
37911  * @constructor
37912  * Creates a new Hidden form element.
37913  * @param {Object} config Configuration options
37914  */
37915
37916
37917
37918 // easy hidden field...
37919 Roo.form.Hidden = function(config){
37920     Roo.form.Hidden.superclass.constructor.call(this, config);
37921 };
37922   
37923 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37924     fieldLabel:      '',
37925     inputType:      'hidden',
37926     width:          50,
37927     allowBlank:     true,
37928     labelSeparator: '',
37929     hidden:         true,
37930     itemCls :       'x-form-item-display-none'
37931
37932
37933 });
37934
37935
37936 /*
37937  * Based on:
37938  * Ext JS Library 1.1.1
37939  * Copyright(c) 2006-2007, Ext JS, LLC.
37940  *
37941  * Originally Released Under LGPL - original licence link has changed is not relivant.
37942  *
37943  * Fork - LGPL
37944  * <script type="text/javascript">
37945  */
37946  
37947 /**
37948  * @class Roo.form.TriggerField
37949  * @extends Roo.form.TextField
37950  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37951  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37952  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37953  * for which you can provide a custom implementation.  For example:
37954  * <pre><code>
37955 var trigger = new Roo.form.TriggerField();
37956 trigger.onTriggerClick = myTriggerFn;
37957 trigger.applyTo('my-field');
37958 </code></pre>
37959  *
37960  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37961  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37962  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37963  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37964  * @constructor
37965  * Create a new TriggerField.
37966  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37967  * to the base TextField)
37968  */
37969 Roo.form.TriggerField = function(config){
37970     this.mimicing = false;
37971     Roo.form.TriggerField.superclass.constructor.call(this, config);
37972 };
37973
37974 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37975     /**
37976      * @cfg {String} triggerClass A CSS class to apply to the trigger
37977      */
37978     /**
37979      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37980      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37981      */
37982     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37983     /**
37984      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37985      */
37986     hideTrigger:false,
37987
37988     /** @cfg {Boolean} grow @hide */
37989     /** @cfg {Number} growMin @hide */
37990     /** @cfg {Number} growMax @hide */
37991
37992     /**
37993      * @hide 
37994      * @method
37995      */
37996     autoSize: Roo.emptyFn,
37997     // private
37998     monitorTab : true,
37999     // private
38000     deferHeight : true,
38001
38002     
38003     actionMode : 'wrap',
38004     // private
38005     onResize : function(w, h){
38006         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38007         if(typeof w == 'number'){
38008             var x = w - this.trigger.getWidth();
38009             this.el.setWidth(this.adjustWidth('input', x));
38010             this.trigger.setStyle('left', x+'px');
38011         }
38012     },
38013
38014     // private
38015     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38016
38017     // private
38018     getResizeEl : function(){
38019         return this.wrap;
38020     },
38021
38022     // private
38023     getPositionEl : function(){
38024         return this.wrap;
38025     },
38026
38027     // private
38028     alignErrorIcon : function(){
38029         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38030     },
38031
38032     // private
38033     onRender : function(ct, position){
38034         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38035         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38036         this.trigger = this.wrap.createChild(this.triggerConfig ||
38037                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38038         if(this.hideTrigger){
38039             this.trigger.setDisplayed(false);
38040         }
38041         this.initTrigger();
38042         if(!this.width){
38043             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38044         }
38045     },
38046
38047     // private
38048     initTrigger : function(){
38049         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38050         this.trigger.addClassOnOver('x-form-trigger-over');
38051         this.trigger.addClassOnClick('x-form-trigger-click');
38052     },
38053
38054     // private
38055     onDestroy : function(){
38056         if(this.trigger){
38057             this.trigger.removeAllListeners();
38058             this.trigger.remove();
38059         }
38060         if(this.wrap){
38061             this.wrap.remove();
38062         }
38063         Roo.form.TriggerField.superclass.onDestroy.call(this);
38064     },
38065
38066     // private
38067     onFocus : function(){
38068         Roo.form.TriggerField.superclass.onFocus.call(this);
38069         if(!this.mimicing){
38070             this.wrap.addClass('x-trigger-wrap-focus');
38071             this.mimicing = true;
38072             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38073             if(this.monitorTab){
38074                 this.el.on("keydown", this.checkTab, this);
38075             }
38076         }
38077     },
38078
38079     // private
38080     checkTab : function(e){
38081         if(e.getKey() == e.TAB){
38082             this.triggerBlur();
38083         }
38084     },
38085
38086     // private
38087     onBlur : function(){
38088         // do nothing
38089     },
38090
38091     // private
38092     mimicBlur : function(e, t){
38093         if(!this.wrap.contains(t) && this.validateBlur()){
38094             this.triggerBlur();
38095         }
38096     },
38097
38098     // private
38099     triggerBlur : function(){
38100         this.mimicing = false;
38101         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38102         if(this.monitorTab){
38103             this.el.un("keydown", this.checkTab, this);
38104         }
38105         this.wrap.removeClass('x-trigger-wrap-focus');
38106         Roo.form.TriggerField.superclass.onBlur.call(this);
38107     },
38108
38109     // private
38110     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38111     validateBlur : function(e, t){
38112         return true;
38113     },
38114
38115     // private
38116     onDisable : function(){
38117         Roo.form.TriggerField.superclass.onDisable.call(this);
38118         if(this.wrap){
38119             this.wrap.addClass('x-item-disabled');
38120         }
38121     },
38122
38123     // private
38124     onEnable : function(){
38125         Roo.form.TriggerField.superclass.onEnable.call(this);
38126         if(this.wrap){
38127             this.wrap.removeClass('x-item-disabled');
38128         }
38129     },
38130
38131     // private
38132     onShow : function(){
38133         var ae = this.getActionEl();
38134         
38135         if(ae){
38136             ae.dom.style.display = '';
38137             ae.dom.style.visibility = 'visible';
38138         }
38139     },
38140
38141     // private
38142     
38143     onHide : function(){
38144         var ae = this.getActionEl();
38145         ae.dom.style.display = 'none';
38146     },
38147
38148     /**
38149      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38150      * by an implementing function.
38151      * @method
38152      * @param {EventObject} e
38153      */
38154     onTriggerClick : Roo.emptyFn
38155 });
38156
38157 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38158 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38159 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38160 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38161     initComponent : function(){
38162         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38163
38164         this.triggerConfig = {
38165             tag:'span', cls:'x-form-twin-triggers', cn:[
38166             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38167             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38168         ]};
38169     },
38170
38171     getTrigger : function(index){
38172         return this.triggers[index];
38173     },
38174
38175     initTrigger : function(){
38176         var ts = this.trigger.select('.x-form-trigger', true);
38177         this.wrap.setStyle('overflow', 'hidden');
38178         var triggerField = this;
38179         ts.each(function(t, all, index){
38180             t.hide = function(){
38181                 var w = triggerField.wrap.getWidth();
38182                 this.dom.style.display = 'none';
38183                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38184             };
38185             t.show = function(){
38186                 var w = triggerField.wrap.getWidth();
38187                 this.dom.style.display = '';
38188                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38189             };
38190             var triggerIndex = 'Trigger'+(index+1);
38191
38192             if(this['hide'+triggerIndex]){
38193                 t.dom.style.display = 'none';
38194             }
38195             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38196             t.addClassOnOver('x-form-trigger-over');
38197             t.addClassOnClick('x-form-trigger-click');
38198         }, this);
38199         this.triggers = ts.elements;
38200     },
38201
38202     onTrigger1Click : Roo.emptyFn,
38203     onTrigger2Click : Roo.emptyFn
38204 });/*
38205  * Based on:
38206  * Ext JS Library 1.1.1
38207  * Copyright(c) 2006-2007, Ext JS, LLC.
38208  *
38209  * Originally Released Under LGPL - original licence link has changed is not relivant.
38210  *
38211  * Fork - LGPL
38212  * <script type="text/javascript">
38213  */
38214  
38215 /**
38216  * @class Roo.form.TextArea
38217  * @extends Roo.form.TextField
38218  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38219  * support for auto-sizing.
38220  * @constructor
38221  * Creates a new TextArea
38222  * @param {Object} config Configuration options
38223  */
38224 Roo.form.TextArea = function(config){
38225     Roo.form.TextArea.superclass.constructor.call(this, config);
38226     // these are provided exchanges for backwards compat
38227     // minHeight/maxHeight were replaced by growMin/growMax to be
38228     // compatible with TextField growing config values
38229     if(this.minHeight !== undefined){
38230         this.growMin = this.minHeight;
38231     }
38232     if(this.maxHeight !== undefined){
38233         this.growMax = this.maxHeight;
38234     }
38235 };
38236
38237 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38238     /**
38239      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38240      */
38241     growMin : 60,
38242     /**
38243      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38244      */
38245     growMax: 1000,
38246     /**
38247      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38248      * in the field (equivalent to setting overflow: hidden, defaults to false)
38249      */
38250     preventScrollbars: false,
38251     /**
38252      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38253      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38254      */
38255
38256     // private
38257     onRender : function(ct, position){
38258         if(!this.el){
38259             this.defaultAutoCreate = {
38260                 tag: "textarea",
38261                 style:"width:300px;height:60px;",
38262                 autocomplete: "off"
38263             };
38264         }
38265         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38266         if(this.grow){
38267             this.textSizeEl = Roo.DomHelper.append(document.body, {
38268                 tag: "pre", cls: "x-form-grow-sizer"
38269             });
38270             if(this.preventScrollbars){
38271                 this.el.setStyle("overflow", "hidden");
38272             }
38273             this.el.setHeight(this.growMin);
38274         }
38275     },
38276
38277     onDestroy : function(){
38278         if(this.textSizeEl){
38279             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38280         }
38281         Roo.form.TextArea.superclass.onDestroy.call(this);
38282     },
38283
38284     // private
38285     onKeyUp : function(e){
38286         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38287             this.autoSize();
38288         }
38289     },
38290
38291     /**
38292      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38293      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38294      */
38295     autoSize : function(){
38296         if(!this.grow || !this.textSizeEl){
38297             return;
38298         }
38299         var el = this.el;
38300         var v = el.dom.value;
38301         var ts = this.textSizeEl;
38302
38303         ts.innerHTML = '';
38304         ts.appendChild(document.createTextNode(v));
38305         v = ts.innerHTML;
38306
38307         Roo.fly(ts).setWidth(this.el.getWidth());
38308         if(v.length < 1){
38309             v = "&#160;&#160;";
38310         }else{
38311             if(Roo.isIE){
38312                 v = v.replace(/\n/g, '<p>&#160;</p>');
38313             }
38314             v += "&#160;\n&#160;";
38315         }
38316         ts.innerHTML = v;
38317         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38318         if(h != this.lastHeight){
38319             this.lastHeight = h;
38320             this.el.setHeight(h);
38321             this.fireEvent("autosize", this, h);
38322         }
38323     }
38324 });/*
38325  * Based on:
38326  * Ext JS Library 1.1.1
38327  * Copyright(c) 2006-2007, Ext JS, LLC.
38328  *
38329  * Originally Released Under LGPL - original licence link has changed is not relivant.
38330  *
38331  * Fork - LGPL
38332  * <script type="text/javascript">
38333  */
38334  
38335
38336 /**
38337  * @class Roo.form.NumberField
38338  * @extends Roo.form.TextField
38339  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38340  * @constructor
38341  * Creates a new NumberField
38342  * @param {Object} config Configuration options
38343  */
38344 Roo.form.NumberField = function(config){
38345     Roo.form.NumberField.superclass.constructor.call(this, config);
38346 };
38347
38348 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38349     /**
38350      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38351      */
38352     fieldClass: "x-form-field x-form-num-field",
38353     /**
38354      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38355      */
38356     allowDecimals : true,
38357     /**
38358      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38359      */
38360     decimalSeparator : ".",
38361     /**
38362      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38363      */
38364     decimalPrecision : 2,
38365     /**
38366      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38367      */
38368     allowNegative : true,
38369     /**
38370      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38371      */
38372     minValue : Number.NEGATIVE_INFINITY,
38373     /**
38374      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38375      */
38376     maxValue : Number.MAX_VALUE,
38377     /**
38378      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38379      */
38380     minText : "The minimum value for this field is {0}",
38381     /**
38382      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38383      */
38384     maxText : "The maximum value for this field is {0}",
38385     /**
38386      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38387      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38388      */
38389     nanText : "{0} is not a valid number",
38390
38391     // private
38392     initEvents : function(){
38393         Roo.form.NumberField.superclass.initEvents.call(this);
38394         var allowed = "0123456789";
38395         if(this.allowDecimals){
38396             allowed += this.decimalSeparator;
38397         }
38398         if(this.allowNegative){
38399             allowed += "-";
38400         }
38401         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38402         var keyPress = function(e){
38403             var k = e.getKey();
38404             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38405                 return;
38406             }
38407             var c = e.getCharCode();
38408             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38409                 e.stopEvent();
38410             }
38411         };
38412         this.el.on("keypress", keyPress, this);
38413     },
38414
38415     // private
38416     validateValue : function(value){
38417         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38418             return false;
38419         }
38420         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38421              return true;
38422         }
38423         var num = this.parseValue(value);
38424         if(isNaN(num)){
38425             this.markInvalid(String.format(this.nanText, value));
38426             return false;
38427         }
38428         if(num < this.minValue){
38429             this.markInvalid(String.format(this.minText, this.minValue));
38430             return false;
38431         }
38432         if(num > this.maxValue){
38433             this.markInvalid(String.format(this.maxText, this.maxValue));
38434             return false;
38435         }
38436         return true;
38437     },
38438
38439     getValue : function(){
38440         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38441     },
38442
38443     // private
38444     parseValue : function(value){
38445         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38446         return isNaN(value) ? '' : value;
38447     },
38448
38449     // private
38450     fixPrecision : function(value){
38451         var nan = isNaN(value);
38452         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38453             return nan ? '' : value;
38454         }
38455         return parseFloat(value).toFixed(this.decimalPrecision);
38456     },
38457
38458     setValue : function(v){
38459         v = this.fixPrecision(v);
38460         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38461     },
38462
38463     // private
38464     decimalPrecisionFcn : function(v){
38465         return Math.floor(v);
38466     },
38467
38468     beforeBlur : function(){
38469         var v = this.parseValue(this.getRawValue());
38470         if(v){
38471             this.setValue(v);
38472         }
38473     }
38474 });/*
38475  * Based on:
38476  * Ext JS Library 1.1.1
38477  * Copyright(c) 2006-2007, Ext JS, LLC.
38478  *
38479  * Originally Released Under LGPL - original licence link has changed is not relivant.
38480  *
38481  * Fork - LGPL
38482  * <script type="text/javascript">
38483  */
38484  
38485 /**
38486  * @class Roo.form.DateField
38487  * @extends Roo.form.TriggerField
38488  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38489 * @constructor
38490 * Create a new DateField
38491 * @param {Object} config
38492  */
38493 Roo.form.DateField = function(config){
38494     Roo.form.DateField.superclass.constructor.call(this, config);
38495     
38496       this.addEvents({
38497          
38498         /**
38499          * @event select
38500          * Fires when a date is selected
38501              * @param {Roo.form.DateField} combo This combo box
38502              * @param {Date} date The date selected
38503              */
38504         'select' : true
38505          
38506     });
38507     
38508     
38509     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38510     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38511     this.ddMatch = null;
38512     if(this.disabledDates){
38513         var dd = this.disabledDates;
38514         var re = "(?:";
38515         for(var i = 0; i < dd.length; i++){
38516             re += dd[i];
38517             if(i != dd.length-1) re += "|";
38518         }
38519         this.ddMatch = new RegExp(re + ")");
38520     }
38521 };
38522
38523 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38524     /**
38525      * @cfg {String} format
38526      * The default date format string which can be overriden for localization support.  The format must be
38527      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38528      */
38529     format : "m/d/y",
38530     /**
38531      * @cfg {String} altFormats
38532      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38533      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38534      */
38535     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38536     /**
38537      * @cfg {Array} disabledDays
38538      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38539      */
38540     disabledDays : null,
38541     /**
38542      * @cfg {String} disabledDaysText
38543      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38544      */
38545     disabledDaysText : "Disabled",
38546     /**
38547      * @cfg {Array} disabledDates
38548      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38549      * expression so they are very powerful. Some examples:
38550      * <ul>
38551      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38552      * <li>["03/08", "09/16"] would disable those days for every year</li>
38553      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38554      * <li>["03/../2006"] would disable every day in March 2006</li>
38555      * <li>["^03"] would disable every day in every March</li>
38556      * </ul>
38557      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38558      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38559      */
38560     disabledDates : null,
38561     /**
38562      * @cfg {String} disabledDatesText
38563      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38564      */
38565     disabledDatesText : "Disabled",
38566     /**
38567      * @cfg {Date/String} minValue
38568      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38569      * valid format (defaults to null).
38570      */
38571     minValue : null,
38572     /**
38573      * @cfg {Date/String} maxValue
38574      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38575      * valid format (defaults to null).
38576      */
38577     maxValue : null,
38578     /**
38579      * @cfg {String} minText
38580      * The error text to display when the date in the cell is before minValue (defaults to
38581      * 'The date in this field must be after {minValue}').
38582      */
38583     minText : "The date in this field must be equal to or after {0}",
38584     /**
38585      * @cfg {String} maxText
38586      * The error text to display when the date in the cell is after maxValue (defaults to
38587      * 'The date in this field must be before {maxValue}').
38588      */
38589     maxText : "The date in this field must be equal to or before {0}",
38590     /**
38591      * @cfg {String} invalidText
38592      * The error text to display when the date in the field is invalid (defaults to
38593      * '{value} is not a valid date - it must be in the format {format}').
38594      */
38595     invalidText : "{0} is not a valid date - it must be in the format {1}",
38596     /**
38597      * @cfg {String} triggerClass
38598      * An additional CSS class used to style the trigger button.  The trigger will always get the
38599      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38600      * which displays a calendar icon).
38601      */
38602     triggerClass : 'x-form-date-trigger',
38603     
38604
38605     /**
38606      * @cfg {Boolean} useIso
38607      * if enabled, then the date field will use a hidden field to store the 
38608      * real value as iso formated date. default (false)
38609      */ 
38610     useIso : false,
38611     /**
38612      * @cfg {String/Object} autoCreate
38613      * A DomHelper element spec, or true for a default element spec (defaults to
38614      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38615      */ 
38616     // private
38617     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38618     
38619     // private
38620     hiddenField: false,
38621     
38622     onRender : function(ct, position)
38623     {
38624         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38625         if (this.useIso) {
38626             //this.el.dom.removeAttribute('name'); 
38627             Roo.log("Changing name?");
38628             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38629             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38630                     'before', true);
38631             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38632             // prevent input submission
38633             this.hiddenName = this.name;
38634         }
38635             
38636             
38637     },
38638     
38639     // private
38640     validateValue : function(value)
38641     {
38642         value = this.formatDate(value);
38643         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38644             Roo.log('super failed');
38645             return false;
38646         }
38647         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38648              return true;
38649         }
38650         var svalue = value;
38651         value = this.parseDate(value);
38652         if(!value){
38653             Roo.log('parse date failed' + svalue);
38654             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38655             return false;
38656         }
38657         var time = value.getTime();
38658         if(this.minValue && time < this.minValue.getTime()){
38659             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38660             return false;
38661         }
38662         if(this.maxValue && time > this.maxValue.getTime()){
38663             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38664             return false;
38665         }
38666         if(this.disabledDays){
38667             var day = value.getDay();
38668             for(var i = 0; i < this.disabledDays.length; i++) {
38669                 if(day === this.disabledDays[i]){
38670                     this.markInvalid(this.disabledDaysText);
38671                     return false;
38672                 }
38673             }
38674         }
38675         var fvalue = this.formatDate(value);
38676         if(this.ddMatch && this.ddMatch.test(fvalue)){
38677             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38678             return false;
38679         }
38680         return true;
38681     },
38682
38683     // private
38684     // Provides logic to override the default TriggerField.validateBlur which just returns true
38685     validateBlur : function(){
38686         return !this.menu || !this.menu.isVisible();
38687     },
38688     
38689     getName: function()
38690     {
38691         // returns hidden if it's set..
38692         if (!this.rendered) {return ''};
38693         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38694         
38695     },
38696
38697     /**
38698      * Returns the current date value of the date field.
38699      * @return {Date} The date value
38700      */
38701     getValue : function(){
38702         
38703         return  this.hiddenField ?
38704                 this.hiddenField.value :
38705                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38706     },
38707
38708     /**
38709      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38710      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38711      * (the default format used is "m/d/y").
38712      * <br />Usage:
38713      * <pre><code>
38714 //All of these calls set the same date value (May 4, 2006)
38715
38716 //Pass a date object:
38717 var dt = new Date('5/4/06');
38718 dateField.setValue(dt);
38719
38720 //Pass a date string (default format):
38721 dateField.setValue('5/4/06');
38722
38723 //Pass a date string (custom format):
38724 dateField.format = 'Y-m-d';
38725 dateField.setValue('2006-5-4');
38726 </code></pre>
38727      * @param {String/Date} date The date or valid date string
38728      */
38729     setValue : function(date){
38730         if (this.hiddenField) {
38731             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38732         }
38733         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38734         // make sure the value field is always stored as a date..
38735         this.value = this.parseDate(date);
38736         
38737         
38738     },
38739
38740     // private
38741     parseDate : function(value){
38742         if(!value || value instanceof Date){
38743             return value;
38744         }
38745         var v = Date.parseDate(value, this.format);
38746          if (!v && this.useIso) {
38747             v = Date.parseDate(value, 'Y-m-d');
38748         }
38749         if(!v && this.altFormats){
38750             if(!this.altFormatsArray){
38751                 this.altFormatsArray = this.altFormats.split("|");
38752             }
38753             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38754                 v = Date.parseDate(value, this.altFormatsArray[i]);
38755             }
38756         }
38757         return v;
38758     },
38759
38760     // private
38761     formatDate : function(date, fmt){
38762         return (!date || !(date instanceof Date)) ?
38763                date : date.dateFormat(fmt || this.format);
38764     },
38765
38766     // private
38767     menuListeners : {
38768         select: function(m, d){
38769             
38770             this.setValue(d);
38771             this.fireEvent('select', this, d);
38772         },
38773         show : function(){ // retain focus styling
38774             this.onFocus();
38775         },
38776         hide : function(){
38777             this.focus.defer(10, this);
38778             var ml = this.menuListeners;
38779             this.menu.un("select", ml.select,  this);
38780             this.menu.un("show", ml.show,  this);
38781             this.menu.un("hide", ml.hide,  this);
38782         }
38783     },
38784
38785     // private
38786     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38787     onTriggerClick : function(){
38788         if(this.disabled){
38789             return;
38790         }
38791         if(this.menu == null){
38792             this.menu = new Roo.menu.DateMenu();
38793         }
38794         Roo.apply(this.menu.picker,  {
38795             showClear: this.allowBlank,
38796             minDate : this.minValue,
38797             maxDate : this.maxValue,
38798             disabledDatesRE : this.ddMatch,
38799             disabledDatesText : this.disabledDatesText,
38800             disabledDays : this.disabledDays,
38801             disabledDaysText : this.disabledDaysText,
38802             format : this.useIso ? 'Y-m-d' : this.format,
38803             minText : String.format(this.minText, this.formatDate(this.minValue)),
38804             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38805         });
38806         this.menu.on(Roo.apply({}, this.menuListeners, {
38807             scope:this
38808         }));
38809         this.menu.picker.setValue(this.getValue() || new Date());
38810         this.menu.show(this.el, "tl-bl?");
38811     },
38812
38813     beforeBlur : function(){
38814         var v = this.parseDate(this.getRawValue());
38815         if(v){
38816             this.setValue(v);
38817         }
38818     },
38819
38820     /*@
38821      * overide
38822      * 
38823      */
38824     isDirty : function() {
38825         if(this.disabled) {
38826             return false;
38827         }
38828         
38829         if(typeof(this.startValue) === 'undefined'){
38830             return false;
38831         }
38832         
38833         return String(this.getValue()) !== String(this.startValue);
38834         
38835     }
38836 });/*
38837  * Based on:
38838  * Ext JS Library 1.1.1
38839  * Copyright(c) 2006-2007, Ext JS, LLC.
38840  *
38841  * Originally Released Under LGPL - original licence link has changed is not relivant.
38842  *
38843  * Fork - LGPL
38844  * <script type="text/javascript">
38845  */
38846  
38847 /**
38848  * @class Roo.form.MonthField
38849  * @extends Roo.form.TriggerField
38850  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38851 * @constructor
38852 * Create a new MonthField
38853 * @param {Object} config
38854  */
38855 Roo.form.MonthField = function(config){
38856     
38857     Roo.form.MonthField.superclass.constructor.call(this, config);
38858     
38859       this.addEvents({
38860          
38861         /**
38862          * @event select
38863          * Fires when a date is selected
38864              * @param {Roo.form.MonthFieeld} combo This combo box
38865              * @param {Date} date The date selected
38866              */
38867         'select' : true
38868          
38869     });
38870     
38871     
38872     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38873     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38874     this.ddMatch = null;
38875     if(this.disabledDates){
38876         var dd = this.disabledDates;
38877         var re = "(?:";
38878         for(var i = 0; i < dd.length; i++){
38879             re += dd[i];
38880             if(i != dd.length-1) re += "|";
38881         }
38882         this.ddMatch = new RegExp(re + ")");
38883     }
38884 };
38885
38886 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38887     /**
38888      * @cfg {String} format
38889      * The default date format string which can be overriden for localization support.  The format must be
38890      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38891      */
38892     format : "M Y",
38893     /**
38894      * @cfg {String} altFormats
38895      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38896      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38897      */
38898     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38899     /**
38900      * @cfg {Array} disabledDays
38901      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38902      */
38903     disabledDays : [0,1,2,3,4,5,6],
38904     /**
38905      * @cfg {String} disabledDaysText
38906      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38907      */
38908     disabledDaysText : "Disabled",
38909     /**
38910      * @cfg {Array} disabledDates
38911      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38912      * expression so they are very powerful. Some examples:
38913      * <ul>
38914      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38915      * <li>["03/08", "09/16"] would disable those days for every year</li>
38916      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38917      * <li>["03/../2006"] would disable every day in March 2006</li>
38918      * <li>["^03"] would disable every day in every March</li>
38919      * </ul>
38920      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38921      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38922      */
38923     disabledDates : null,
38924     /**
38925      * @cfg {String} disabledDatesText
38926      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38927      */
38928     disabledDatesText : "Disabled",
38929     /**
38930      * @cfg {Date/String} minValue
38931      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38932      * valid format (defaults to null).
38933      */
38934     minValue : null,
38935     /**
38936      * @cfg {Date/String} maxValue
38937      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38938      * valid format (defaults to null).
38939      */
38940     maxValue : null,
38941     /**
38942      * @cfg {String} minText
38943      * The error text to display when the date in the cell is before minValue (defaults to
38944      * 'The date in this field must be after {minValue}').
38945      */
38946     minText : "The date in this field must be equal to or after {0}",
38947     /**
38948      * @cfg {String} maxTextf
38949      * The error text to display when the date in the cell is after maxValue (defaults to
38950      * 'The date in this field must be before {maxValue}').
38951      */
38952     maxText : "The date in this field must be equal to or before {0}",
38953     /**
38954      * @cfg {String} invalidText
38955      * The error text to display when the date in the field is invalid (defaults to
38956      * '{value} is not a valid date - it must be in the format {format}').
38957      */
38958     invalidText : "{0} is not a valid date - it must be in the format {1}",
38959     /**
38960      * @cfg {String} triggerClass
38961      * An additional CSS class used to style the trigger button.  The trigger will always get the
38962      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38963      * which displays a calendar icon).
38964      */
38965     triggerClass : 'x-form-date-trigger',
38966     
38967
38968     /**
38969      * @cfg {Boolean} useIso
38970      * if enabled, then the date field will use a hidden field to store the 
38971      * real value as iso formated date. default (true)
38972      */ 
38973     useIso : true,
38974     /**
38975      * @cfg {String/Object} autoCreate
38976      * A DomHelper element spec, or true for a default element spec (defaults to
38977      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38978      */ 
38979     // private
38980     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38981     
38982     // private
38983     hiddenField: false,
38984     
38985     hideMonthPicker : false,
38986     
38987     onRender : function(ct, position)
38988     {
38989         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38990         if (this.useIso) {
38991             this.el.dom.removeAttribute('name'); 
38992             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38993                     'before', true);
38994             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38995             // prevent input submission
38996             this.hiddenName = this.name;
38997         }
38998             
38999             
39000     },
39001     
39002     // private
39003     validateValue : function(value)
39004     {
39005         value = this.formatDate(value);
39006         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39007             return false;
39008         }
39009         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39010              return true;
39011         }
39012         var svalue = value;
39013         value = this.parseDate(value);
39014         if(!value){
39015             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39016             return false;
39017         }
39018         var time = value.getTime();
39019         if(this.minValue && time < this.minValue.getTime()){
39020             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39021             return false;
39022         }
39023         if(this.maxValue && time > this.maxValue.getTime()){
39024             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39025             return false;
39026         }
39027         /*if(this.disabledDays){
39028             var day = value.getDay();
39029             for(var i = 0; i < this.disabledDays.length; i++) {
39030                 if(day === this.disabledDays[i]){
39031                     this.markInvalid(this.disabledDaysText);
39032                     return false;
39033                 }
39034             }
39035         }
39036         */
39037         var fvalue = this.formatDate(value);
39038         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39039             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39040             return false;
39041         }
39042         */
39043         return true;
39044     },
39045
39046     // private
39047     // Provides logic to override the default TriggerField.validateBlur which just returns true
39048     validateBlur : function(){
39049         return !this.menu || !this.menu.isVisible();
39050     },
39051
39052     /**
39053      * Returns the current date value of the date field.
39054      * @return {Date} The date value
39055      */
39056     getValue : function(){
39057         
39058         
39059         
39060         return  this.hiddenField ?
39061                 this.hiddenField.value :
39062                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39063     },
39064
39065     /**
39066      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39067      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39068      * (the default format used is "m/d/y").
39069      * <br />Usage:
39070      * <pre><code>
39071 //All of these calls set the same date value (May 4, 2006)
39072
39073 //Pass a date object:
39074 var dt = new Date('5/4/06');
39075 monthField.setValue(dt);
39076
39077 //Pass a date string (default format):
39078 monthField.setValue('5/4/06');
39079
39080 //Pass a date string (custom format):
39081 monthField.format = 'Y-m-d';
39082 monthField.setValue('2006-5-4');
39083 </code></pre>
39084      * @param {String/Date} date The date or valid date string
39085      */
39086     setValue : function(date){
39087         Roo.log('month setValue' + date);
39088         // can only be first of month..
39089         
39090         var val = this.parseDate(date);
39091         
39092         if (this.hiddenField) {
39093             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39094         }
39095         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39096         this.value = this.parseDate(date);
39097     },
39098
39099     // private
39100     parseDate : function(value){
39101         if(!value || value instanceof Date){
39102             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39103             return value;
39104         }
39105         var v = Date.parseDate(value, this.format);
39106         if (!v && this.useIso) {
39107             v = Date.parseDate(value, 'Y-m-d');
39108         }
39109         if (v) {
39110             // 
39111             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39112         }
39113         
39114         
39115         if(!v && this.altFormats){
39116             if(!this.altFormatsArray){
39117                 this.altFormatsArray = this.altFormats.split("|");
39118             }
39119             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39120                 v = Date.parseDate(value, this.altFormatsArray[i]);
39121             }
39122         }
39123         return v;
39124     },
39125
39126     // private
39127     formatDate : function(date, fmt){
39128         return (!date || !(date instanceof Date)) ?
39129                date : date.dateFormat(fmt || this.format);
39130     },
39131
39132     // private
39133     menuListeners : {
39134         select: function(m, d){
39135             this.setValue(d);
39136             this.fireEvent('select', this, d);
39137         },
39138         show : function(){ // retain focus styling
39139             this.onFocus();
39140         },
39141         hide : function(){
39142             this.focus.defer(10, this);
39143             var ml = this.menuListeners;
39144             this.menu.un("select", ml.select,  this);
39145             this.menu.un("show", ml.show,  this);
39146             this.menu.un("hide", ml.hide,  this);
39147         }
39148     },
39149     // private
39150     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39151     onTriggerClick : function(){
39152         if(this.disabled){
39153             return;
39154         }
39155         if(this.menu == null){
39156             this.menu = new Roo.menu.DateMenu();
39157            
39158         }
39159         
39160         Roo.apply(this.menu.picker,  {
39161             
39162             showClear: this.allowBlank,
39163             minDate : this.minValue,
39164             maxDate : this.maxValue,
39165             disabledDatesRE : this.ddMatch,
39166             disabledDatesText : this.disabledDatesText,
39167             
39168             format : this.useIso ? 'Y-m-d' : this.format,
39169             minText : String.format(this.minText, this.formatDate(this.minValue)),
39170             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39171             
39172         });
39173          this.menu.on(Roo.apply({}, this.menuListeners, {
39174             scope:this
39175         }));
39176        
39177         
39178         var m = this.menu;
39179         var p = m.picker;
39180         
39181         // hide month picker get's called when we called by 'before hide';
39182         
39183         var ignorehide = true;
39184         p.hideMonthPicker  = function(disableAnim){
39185             if (ignorehide) {
39186                 return;
39187             }
39188              if(this.monthPicker){
39189                 Roo.log("hideMonthPicker called");
39190                 if(disableAnim === true){
39191                     this.monthPicker.hide();
39192                 }else{
39193                     this.monthPicker.slideOut('t', {duration:.2});
39194                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39195                     p.fireEvent("select", this, this.value);
39196                     m.hide();
39197                 }
39198             }
39199         }
39200         
39201         Roo.log('picker set value');
39202         Roo.log(this.getValue());
39203         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39204         m.show(this.el, 'tl-bl?');
39205         ignorehide  = false;
39206         // this will trigger hideMonthPicker..
39207         
39208         
39209         // hidden the day picker
39210         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39211         
39212         
39213         
39214       
39215         
39216         p.showMonthPicker.defer(100, p);
39217     
39218         
39219        
39220     },
39221
39222     beforeBlur : function(){
39223         var v = this.parseDate(this.getRawValue());
39224         if(v){
39225             this.setValue(v);
39226         }
39227     }
39228
39229     /** @cfg {Boolean} grow @hide */
39230     /** @cfg {Number} growMin @hide */
39231     /** @cfg {Number} growMax @hide */
39232     /**
39233      * @hide
39234      * @method autoSize
39235      */
39236 });/*
39237  * Based on:
39238  * Ext JS Library 1.1.1
39239  * Copyright(c) 2006-2007, Ext JS, LLC.
39240  *
39241  * Originally Released Under LGPL - original licence link has changed is not relivant.
39242  *
39243  * Fork - LGPL
39244  * <script type="text/javascript">
39245  */
39246  
39247
39248 /**
39249  * @class Roo.form.ComboBox
39250  * @extends Roo.form.TriggerField
39251  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39252  * @constructor
39253  * Create a new ComboBox.
39254  * @param {Object} config Configuration options
39255  */
39256 Roo.form.ComboBox = function(config){
39257     Roo.form.ComboBox.superclass.constructor.call(this, config);
39258     this.addEvents({
39259         /**
39260          * @event expand
39261          * Fires when the dropdown list is expanded
39262              * @param {Roo.form.ComboBox} combo This combo box
39263              */
39264         'expand' : true,
39265         /**
39266          * @event collapse
39267          * Fires when the dropdown list is collapsed
39268              * @param {Roo.form.ComboBox} combo This combo box
39269              */
39270         'collapse' : true,
39271         /**
39272          * @event beforeselect
39273          * Fires before a list item is selected. Return false to cancel the selection.
39274              * @param {Roo.form.ComboBox} combo This combo box
39275              * @param {Roo.data.Record} record The data record returned from the underlying store
39276              * @param {Number} index The index of the selected item in the dropdown list
39277              */
39278         'beforeselect' : true,
39279         /**
39280          * @event select
39281          * Fires when a list item is selected
39282              * @param {Roo.form.ComboBox} combo This combo box
39283              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39284              * @param {Number} index The index of the selected item in the dropdown list
39285              */
39286         'select' : true,
39287         /**
39288          * @event beforequery
39289          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39290          * The event object passed has these properties:
39291              * @param {Roo.form.ComboBox} combo This combo box
39292              * @param {String} query The query
39293              * @param {Boolean} forceAll true to force "all" query
39294              * @param {Boolean} cancel true to cancel the query
39295              * @param {Object} e The query event object
39296              */
39297         'beforequery': true,
39298          /**
39299          * @event add
39300          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39301              * @param {Roo.form.ComboBox} combo This combo box
39302              */
39303         'add' : true,
39304         /**
39305          * @event edit
39306          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39307              * @param {Roo.form.ComboBox} combo This combo box
39308              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39309              */
39310         'edit' : true
39311         
39312         
39313     });
39314     if(this.transform){
39315         this.allowDomMove = false;
39316         var s = Roo.getDom(this.transform);
39317         if(!this.hiddenName){
39318             this.hiddenName = s.name;
39319         }
39320         if(!this.store){
39321             this.mode = 'local';
39322             var d = [], opts = s.options;
39323             for(var i = 0, len = opts.length;i < len; i++){
39324                 var o = opts[i];
39325                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39326                 if(o.selected) {
39327                     this.value = value;
39328                 }
39329                 d.push([value, o.text]);
39330             }
39331             this.store = new Roo.data.SimpleStore({
39332                 'id': 0,
39333                 fields: ['value', 'text'],
39334                 data : d
39335             });
39336             this.valueField = 'value';
39337             this.displayField = 'text';
39338         }
39339         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39340         if(!this.lazyRender){
39341             this.target = true;
39342             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39343             s.parentNode.removeChild(s); // remove it
39344             this.render(this.el.parentNode);
39345         }else{
39346             s.parentNode.removeChild(s); // remove it
39347         }
39348
39349     }
39350     if (this.store) {
39351         this.store = Roo.factory(this.store, Roo.data);
39352     }
39353     
39354     this.selectedIndex = -1;
39355     if(this.mode == 'local'){
39356         if(config.queryDelay === undefined){
39357             this.queryDelay = 10;
39358         }
39359         if(config.minChars === undefined){
39360             this.minChars = 0;
39361         }
39362     }
39363 };
39364
39365 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39366     /**
39367      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39368      */
39369     /**
39370      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39371      * rendering into an Roo.Editor, defaults to false)
39372      */
39373     /**
39374      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39375      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39376      */
39377     /**
39378      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39379      */
39380     /**
39381      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39382      * the dropdown list (defaults to undefined, with no header element)
39383      */
39384
39385      /**
39386      * @cfg {String/Roo.Template} tpl The template to use to render the output
39387      */
39388      
39389     // private
39390     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39391     /**
39392      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39393      */
39394     listWidth: undefined,
39395     /**
39396      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39397      * mode = 'remote' or 'text' if mode = 'local')
39398      */
39399     displayField: undefined,
39400     /**
39401      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39402      * mode = 'remote' or 'value' if mode = 'local'). 
39403      * Note: use of a valueField requires the user make a selection
39404      * in order for a value to be mapped.
39405      */
39406     valueField: undefined,
39407     
39408     
39409     /**
39410      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39411      * field's data value (defaults to the underlying DOM element's name)
39412      */
39413     hiddenName: undefined,
39414     /**
39415      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39416      */
39417     listClass: '',
39418     /**
39419      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39420      */
39421     selectedClass: 'x-combo-selected',
39422     /**
39423      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39424      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39425      * which displays a downward arrow icon).
39426      */
39427     triggerClass : 'x-form-arrow-trigger',
39428     /**
39429      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39430      */
39431     shadow:'sides',
39432     /**
39433      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39434      * anchor positions (defaults to 'tl-bl')
39435      */
39436     listAlign: 'tl-bl?',
39437     /**
39438      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39439      */
39440     maxHeight: 300,
39441     /**
39442      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39443      * query specified by the allQuery config option (defaults to 'query')
39444      */
39445     triggerAction: 'query',
39446     /**
39447      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39448      * (defaults to 4, does not apply if editable = false)
39449      */
39450     minChars : 4,
39451     /**
39452      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39453      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39454      */
39455     typeAhead: false,
39456     /**
39457      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39458      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39459      */
39460     queryDelay: 500,
39461     /**
39462      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39463      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39464      */
39465     pageSize: 0,
39466     /**
39467      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39468      * when editable = true (defaults to false)
39469      */
39470     selectOnFocus:false,
39471     /**
39472      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39473      */
39474     queryParam: 'query',
39475     /**
39476      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39477      * when mode = 'remote' (defaults to 'Loading...')
39478      */
39479     loadingText: 'Loading...',
39480     /**
39481      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39482      */
39483     resizable: false,
39484     /**
39485      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39486      */
39487     handleHeight : 8,
39488     /**
39489      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39490      * traditional select (defaults to true)
39491      */
39492     editable: true,
39493     /**
39494      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39495      */
39496     allQuery: '',
39497     /**
39498      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39499      */
39500     mode: 'remote',
39501     /**
39502      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39503      * listWidth has a higher value)
39504      */
39505     minListWidth : 70,
39506     /**
39507      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39508      * allow the user to set arbitrary text into the field (defaults to false)
39509      */
39510     forceSelection:false,
39511     /**
39512      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39513      * if typeAhead = true (defaults to 250)
39514      */
39515     typeAheadDelay : 250,
39516     /**
39517      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39518      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39519      */
39520     valueNotFoundText : undefined,
39521     /**
39522      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39523      */
39524     blockFocus : false,
39525     
39526     /**
39527      * @cfg {Boolean} disableClear Disable showing of clear button.
39528      */
39529     disableClear : false,
39530     /**
39531      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39532      */
39533     alwaysQuery : false,
39534     
39535     //private
39536     addicon : false,
39537     editicon: false,
39538     
39539     // element that contains real text value.. (when hidden is used..)
39540      
39541     // private
39542     onRender : function(ct, position){
39543         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39544         if(this.hiddenName){
39545             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39546                     'before', true);
39547             this.hiddenField.value =
39548                 this.hiddenValue !== undefined ? this.hiddenValue :
39549                 this.value !== undefined ? this.value : '';
39550
39551             // prevent input submission
39552             this.el.dom.removeAttribute('name');
39553              
39554              
39555         }
39556         if(Roo.isGecko){
39557             this.el.dom.setAttribute('autocomplete', 'off');
39558         }
39559
39560         var cls = 'x-combo-list';
39561
39562         this.list = new Roo.Layer({
39563             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39564         });
39565
39566         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39567         this.list.setWidth(lw);
39568         this.list.swallowEvent('mousewheel');
39569         this.assetHeight = 0;
39570
39571         if(this.title){
39572             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39573             this.assetHeight += this.header.getHeight();
39574         }
39575
39576         this.innerList = this.list.createChild({cls:cls+'-inner'});
39577         this.innerList.on('mouseover', this.onViewOver, this);
39578         this.innerList.on('mousemove', this.onViewMove, this);
39579         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39580         
39581         if(this.allowBlank && !this.pageSize && !this.disableClear){
39582             this.footer = this.list.createChild({cls:cls+'-ft'});
39583             this.pageTb = new Roo.Toolbar(this.footer);
39584            
39585         }
39586         if(this.pageSize){
39587             this.footer = this.list.createChild({cls:cls+'-ft'});
39588             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39589                     {pageSize: this.pageSize});
39590             
39591         }
39592         
39593         if (this.pageTb && this.allowBlank && !this.disableClear) {
39594             var _this = this;
39595             this.pageTb.add(new Roo.Toolbar.Fill(), {
39596                 cls: 'x-btn-icon x-btn-clear',
39597                 text: '&#160;',
39598                 handler: function()
39599                 {
39600                     _this.collapse();
39601                     _this.clearValue();
39602                     _this.onSelect(false, -1);
39603                 }
39604             });
39605         }
39606         if (this.footer) {
39607             this.assetHeight += this.footer.getHeight();
39608         }
39609         
39610
39611         if(!this.tpl){
39612             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39613         }
39614
39615         this.view = new Roo.View(this.innerList, this.tpl, {
39616             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39617         });
39618
39619         this.view.on('click', this.onViewClick, this);
39620
39621         this.store.on('beforeload', this.onBeforeLoad, this);
39622         this.store.on('load', this.onLoad, this);
39623         this.store.on('loadexception', this.onLoadException, this);
39624
39625         if(this.resizable){
39626             this.resizer = new Roo.Resizable(this.list,  {
39627                pinned:true, handles:'se'
39628             });
39629             this.resizer.on('resize', function(r, w, h){
39630                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39631                 this.listWidth = w;
39632                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39633                 this.restrictHeight();
39634             }, this);
39635             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39636         }
39637         if(!this.editable){
39638             this.editable = true;
39639             this.setEditable(false);
39640         }  
39641         
39642         
39643         if (typeof(this.events.add.listeners) != 'undefined') {
39644             
39645             this.addicon = this.wrap.createChild(
39646                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39647        
39648             this.addicon.on('click', function(e) {
39649                 this.fireEvent('add', this);
39650             }, this);
39651         }
39652         if (typeof(this.events.edit.listeners) != 'undefined') {
39653             
39654             this.editicon = this.wrap.createChild(
39655                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39656             if (this.addicon) {
39657                 this.editicon.setStyle('margin-left', '40px');
39658             }
39659             this.editicon.on('click', function(e) {
39660                 
39661                 // we fire even  if inothing is selected..
39662                 this.fireEvent('edit', this, this.lastData );
39663                 
39664             }, this);
39665         }
39666         
39667         
39668         
39669     },
39670
39671     // private
39672     initEvents : function(){
39673         Roo.form.ComboBox.superclass.initEvents.call(this);
39674
39675         this.keyNav = new Roo.KeyNav(this.el, {
39676             "up" : function(e){
39677                 this.inKeyMode = true;
39678                 this.selectPrev();
39679             },
39680
39681             "down" : function(e){
39682                 if(!this.isExpanded()){
39683                     this.onTriggerClick();
39684                 }else{
39685                     this.inKeyMode = true;
39686                     this.selectNext();
39687                 }
39688             },
39689
39690             "enter" : function(e){
39691                 this.onViewClick();
39692                 //return true;
39693             },
39694
39695             "esc" : function(e){
39696                 this.collapse();
39697             },
39698
39699             "tab" : function(e){
39700                 this.onViewClick(false);
39701                 this.fireEvent("specialkey", this, e);
39702                 return true;
39703             },
39704
39705             scope : this,
39706
39707             doRelay : function(foo, bar, hname){
39708                 if(hname == 'down' || this.scope.isExpanded()){
39709                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39710                 }
39711                 return true;
39712             },
39713
39714             forceKeyDown: true
39715         });
39716         this.queryDelay = Math.max(this.queryDelay || 10,
39717                 this.mode == 'local' ? 10 : 250);
39718         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39719         if(this.typeAhead){
39720             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39721         }
39722         if(this.editable !== false){
39723             this.el.on("keyup", this.onKeyUp, this);
39724         }
39725         if(this.forceSelection){
39726             this.on('blur', this.doForce, this);
39727         }
39728     },
39729
39730     onDestroy : function(){
39731         if(this.view){
39732             this.view.setStore(null);
39733             this.view.el.removeAllListeners();
39734             this.view.el.remove();
39735             this.view.purgeListeners();
39736         }
39737         if(this.list){
39738             this.list.destroy();
39739         }
39740         if(this.store){
39741             this.store.un('beforeload', this.onBeforeLoad, this);
39742             this.store.un('load', this.onLoad, this);
39743             this.store.un('loadexception', this.onLoadException, this);
39744         }
39745         Roo.form.ComboBox.superclass.onDestroy.call(this);
39746     },
39747
39748     // private
39749     fireKey : function(e){
39750         if(e.isNavKeyPress() && !this.list.isVisible()){
39751             this.fireEvent("specialkey", this, e);
39752         }
39753     },
39754
39755     // private
39756     onResize: function(w, h){
39757         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39758         
39759         if(typeof w != 'number'){
39760             // we do not handle it!?!?
39761             return;
39762         }
39763         var tw = this.trigger.getWidth();
39764         tw += this.addicon ? this.addicon.getWidth() : 0;
39765         tw += this.editicon ? this.editicon.getWidth() : 0;
39766         var x = w - tw;
39767         this.el.setWidth( this.adjustWidth('input', x));
39768             
39769         this.trigger.setStyle('left', x+'px');
39770         
39771         if(this.list && this.listWidth === undefined){
39772             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39773             this.list.setWidth(lw);
39774             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39775         }
39776         
39777     
39778         
39779     },
39780
39781     /**
39782      * Allow or prevent the user from directly editing the field text.  If false is passed,
39783      * the user will only be able to select from the items defined in the dropdown list.  This method
39784      * is the runtime equivalent of setting the 'editable' config option at config time.
39785      * @param {Boolean} value True to allow the user to directly edit the field text
39786      */
39787     setEditable : function(value){
39788         if(value == this.editable){
39789             return;
39790         }
39791         this.editable = value;
39792         if(!value){
39793             this.el.dom.setAttribute('readOnly', true);
39794             this.el.on('mousedown', this.onTriggerClick,  this);
39795             this.el.addClass('x-combo-noedit');
39796         }else{
39797             this.el.dom.setAttribute('readOnly', false);
39798             this.el.un('mousedown', this.onTriggerClick,  this);
39799             this.el.removeClass('x-combo-noedit');
39800         }
39801     },
39802
39803     // private
39804     onBeforeLoad : function(){
39805         if(!this.hasFocus){
39806             return;
39807         }
39808         this.innerList.update(this.loadingText ?
39809                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39810         this.restrictHeight();
39811         this.selectedIndex = -1;
39812     },
39813
39814     // private
39815     onLoad : function(){
39816         if(!this.hasFocus){
39817             return;
39818         }
39819         if(this.store.getCount() > 0){
39820             this.expand();
39821             this.restrictHeight();
39822             if(this.lastQuery == this.allQuery){
39823                 if(this.editable){
39824                     this.el.dom.select();
39825                 }
39826                 if(!this.selectByValue(this.value, true)){
39827                     this.select(0, true);
39828                 }
39829             }else{
39830                 this.selectNext();
39831                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39832                     this.taTask.delay(this.typeAheadDelay);
39833                 }
39834             }
39835         }else{
39836             this.onEmptyResults();
39837         }
39838         //this.el.focus();
39839     },
39840     // private
39841     onLoadException : function()
39842     {
39843         this.collapse();
39844         Roo.log(this.store.reader.jsonData);
39845         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39846             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39847         }
39848         
39849         
39850     },
39851     // private
39852     onTypeAhead : function(){
39853         if(this.store.getCount() > 0){
39854             var r = this.store.getAt(0);
39855             var newValue = r.data[this.displayField];
39856             var len = newValue.length;
39857             var selStart = this.getRawValue().length;
39858             if(selStart != len){
39859                 this.setRawValue(newValue);
39860                 this.selectText(selStart, newValue.length);
39861             }
39862         }
39863     },
39864
39865     // private
39866     onSelect : function(record, index){
39867         if(this.fireEvent('beforeselect', this, record, index) !== false){
39868             this.setFromData(index > -1 ? record.data : false);
39869             this.collapse();
39870             this.fireEvent('select', this, record, index);
39871         }
39872     },
39873
39874     /**
39875      * Returns the currently selected field value or empty string if no value is set.
39876      * @return {String} value The selected value
39877      */
39878     getValue : function(){
39879         if(this.valueField){
39880             return typeof this.value != 'undefined' ? this.value : '';
39881         }
39882         return Roo.form.ComboBox.superclass.getValue.call(this);
39883     },
39884
39885     /**
39886      * Clears any text/value currently set in the field
39887      */
39888     clearValue : function(){
39889         if(this.hiddenField){
39890             this.hiddenField.value = '';
39891         }
39892         this.value = '';
39893         this.setRawValue('');
39894         this.lastSelectionText = '';
39895         
39896     },
39897
39898     /**
39899      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39900      * will be displayed in the field.  If the value does not match the data value of an existing item,
39901      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39902      * Otherwise the field will be blank (although the value will still be set).
39903      * @param {String} value The value to match
39904      */
39905     setValue : function(v){
39906         var text = v;
39907         if(this.valueField){
39908             var r = this.findRecord(this.valueField, v);
39909             if(r){
39910                 text = r.data[this.displayField];
39911             }else if(this.valueNotFoundText !== undefined){
39912                 text = this.valueNotFoundText;
39913             }
39914         }
39915         this.lastSelectionText = text;
39916         if(this.hiddenField){
39917             this.hiddenField.value = v;
39918         }
39919         Roo.form.ComboBox.superclass.setValue.call(this, text);
39920         this.value = v;
39921     },
39922     /**
39923      * @property {Object} the last set data for the element
39924      */
39925     
39926     lastData : false,
39927     /**
39928      * Sets the value of the field based on a object which is related to the record format for the store.
39929      * @param {Object} value the value to set as. or false on reset?
39930      */
39931     setFromData : function(o){
39932         var dv = ''; // display value
39933         var vv = ''; // value value..
39934         this.lastData = o;
39935         if (this.displayField) {
39936             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39937         } else {
39938             // this is an error condition!!!
39939             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39940         }
39941         
39942         if(this.valueField){
39943             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39944         }
39945         if(this.hiddenField){
39946             this.hiddenField.value = vv;
39947             
39948             this.lastSelectionText = dv;
39949             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39950             this.value = vv;
39951             return;
39952         }
39953         // no hidden field.. - we store the value in 'value', but still display
39954         // display field!!!!
39955         this.lastSelectionText = dv;
39956         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39957         this.value = vv;
39958         
39959         
39960     },
39961     // private
39962     reset : function(){
39963         // overridden so that last data is reset..
39964         this.setValue(this.resetValue);
39965         this.clearInvalid();
39966         this.lastData = false;
39967         if (this.view) {
39968             this.view.clearSelections();
39969         }
39970     },
39971     // private
39972     findRecord : function(prop, value){
39973         var record;
39974         if(this.store.getCount() > 0){
39975             this.store.each(function(r){
39976                 if(r.data[prop] == value){
39977                     record = r;
39978                     return false;
39979                 }
39980                 return true;
39981             });
39982         }
39983         return record;
39984     },
39985     
39986     getName: function()
39987     {
39988         // returns hidden if it's set..
39989         if (!this.rendered) {return ''};
39990         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39991         
39992     },
39993     // private
39994     onViewMove : function(e, t){
39995         this.inKeyMode = false;
39996     },
39997
39998     // private
39999     onViewOver : function(e, t){
40000         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40001             return;
40002         }
40003         var item = this.view.findItemFromChild(t);
40004         if(item){
40005             var index = this.view.indexOf(item);
40006             this.select(index, false);
40007         }
40008     },
40009
40010     // private
40011     onViewClick : function(doFocus)
40012     {
40013         var index = this.view.getSelectedIndexes()[0];
40014         var r = this.store.getAt(index);
40015         if(r){
40016             this.onSelect(r, index);
40017         }
40018         if(doFocus !== false && !this.blockFocus){
40019             this.el.focus();
40020         }
40021     },
40022
40023     // private
40024     restrictHeight : function(){
40025         this.innerList.dom.style.height = '';
40026         var inner = this.innerList.dom;
40027         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40028         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40029         this.list.beginUpdate();
40030         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40031         this.list.alignTo(this.el, this.listAlign);
40032         this.list.endUpdate();
40033     },
40034
40035     // private
40036     onEmptyResults : function(){
40037         this.collapse();
40038     },
40039
40040     /**
40041      * Returns true if the dropdown list is expanded, else false.
40042      */
40043     isExpanded : function(){
40044         return this.list.isVisible();
40045     },
40046
40047     /**
40048      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40049      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40050      * @param {String} value The data value of the item to select
40051      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40052      * selected item if it is not currently in view (defaults to true)
40053      * @return {Boolean} True if the value matched an item in the list, else false
40054      */
40055     selectByValue : function(v, scrollIntoView){
40056         if(v !== undefined && v !== null){
40057             var r = this.findRecord(this.valueField || this.displayField, v);
40058             if(r){
40059                 this.select(this.store.indexOf(r), scrollIntoView);
40060                 return true;
40061             }
40062         }
40063         return false;
40064     },
40065
40066     /**
40067      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40068      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40069      * @param {Number} index The zero-based index of the list item to select
40070      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40071      * selected item if it is not currently in view (defaults to true)
40072      */
40073     select : function(index, scrollIntoView){
40074         this.selectedIndex = index;
40075         this.view.select(index);
40076         if(scrollIntoView !== false){
40077             var el = this.view.getNode(index);
40078             if(el){
40079                 this.innerList.scrollChildIntoView(el, false);
40080             }
40081         }
40082     },
40083
40084     // private
40085     selectNext : function(){
40086         var ct = this.store.getCount();
40087         if(ct > 0){
40088             if(this.selectedIndex == -1){
40089                 this.select(0);
40090             }else if(this.selectedIndex < ct-1){
40091                 this.select(this.selectedIndex+1);
40092             }
40093         }
40094     },
40095
40096     // private
40097     selectPrev : function(){
40098         var ct = this.store.getCount();
40099         if(ct > 0){
40100             if(this.selectedIndex == -1){
40101                 this.select(0);
40102             }else if(this.selectedIndex != 0){
40103                 this.select(this.selectedIndex-1);
40104             }
40105         }
40106     },
40107
40108     // private
40109     onKeyUp : function(e){
40110         if(this.editable !== false && !e.isSpecialKey()){
40111             this.lastKey = e.getKey();
40112             this.dqTask.delay(this.queryDelay);
40113         }
40114     },
40115
40116     // private
40117     validateBlur : function(){
40118         return !this.list || !this.list.isVisible();   
40119     },
40120
40121     // private
40122     initQuery : function(){
40123         this.doQuery(this.getRawValue());
40124     },
40125
40126     // private
40127     doForce : function(){
40128         if(this.el.dom.value.length > 0){
40129             this.el.dom.value =
40130                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40131              
40132         }
40133     },
40134
40135     /**
40136      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40137      * query allowing the query action to be canceled if needed.
40138      * @param {String} query The SQL query to execute
40139      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40140      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40141      * saved in the current store (defaults to false)
40142      */
40143     doQuery : function(q, forceAll){
40144         if(q === undefined || q === null){
40145             q = '';
40146         }
40147         var qe = {
40148             query: q,
40149             forceAll: forceAll,
40150             combo: this,
40151             cancel:false
40152         };
40153         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40154             return false;
40155         }
40156         q = qe.query;
40157         forceAll = qe.forceAll;
40158         if(forceAll === true || (q.length >= this.minChars)){
40159             if(this.lastQuery != q || this.alwaysQuery){
40160                 this.lastQuery = q;
40161                 if(this.mode == 'local'){
40162                     this.selectedIndex = -1;
40163                     if(forceAll){
40164                         this.store.clearFilter();
40165                     }else{
40166                         this.store.filter(this.displayField, q);
40167                     }
40168                     this.onLoad();
40169                 }else{
40170                     this.store.baseParams[this.queryParam] = q;
40171                     this.store.load({
40172                         params: this.getParams(q)
40173                     });
40174                     this.expand();
40175                 }
40176             }else{
40177                 this.selectedIndex = -1;
40178                 this.onLoad();   
40179             }
40180         }
40181     },
40182
40183     // private
40184     getParams : function(q){
40185         var p = {};
40186         //p[this.queryParam] = q;
40187         if(this.pageSize){
40188             p.start = 0;
40189             p.limit = this.pageSize;
40190         }
40191         return p;
40192     },
40193
40194     /**
40195      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40196      */
40197     collapse : function(){
40198         if(!this.isExpanded()){
40199             return;
40200         }
40201         this.list.hide();
40202         Roo.get(document).un('mousedown', this.collapseIf, this);
40203         Roo.get(document).un('mousewheel', this.collapseIf, this);
40204         if (!this.editable) {
40205             Roo.get(document).un('keydown', this.listKeyPress, this);
40206         }
40207         this.fireEvent('collapse', this);
40208     },
40209
40210     // private
40211     collapseIf : function(e){
40212         if(!e.within(this.wrap) && !e.within(this.list)){
40213             this.collapse();
40214         }
40215     },
40216
40217     /**
40218      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40219      */
40220     expand : function(){
40221         if(this.isExpanded() || !this.hasFocus){
40222             return;
40223         }
40224         this.list.alignTo(this.el, this.listAlign);
40225         this.list.show();
40226         Roo.get(document).on('mousedown', this.collapseIf, this);
40227         Roo.get(document).on('mousewheel', this.collapseIf, this);
40228         if (!this.editable) {
40229             Roo.get(document).on('keydown', this.listKeyPress, this);
40230         }
40231         
40232         this.fireEvent('expand', this);
40233     },
40234
40235     // private
40236     // Implements the default empty TriggerField.onTriggerClick function
40237     onTriggerClick : function(){
40238         if(this.disabled){
40239             return;
40240         }
40241         if(this.isExpanded()){
40242             this.collapse();
40243             if (!this.blockFocus) {
40244                 this.el.focus();
40245             }
40246             
40247         }else {
40248             this.hasFocus = true;
40249             if(this.triggerAction == 'all') {
40250                 this.doQuery(this.allQuery, true);
40251             } else {
40252                 this.doQuery(this.getRawValue());
40253             }
40254             if (!this.blockFocus) {
40255                 this.el.focus();
40256             }
40257         }
40258     },
40259     listKeyPress : function(e)
40260     {
40261         //Roo.log('listkeypress');
40262         // scroll to first matching element based on key pres..
40263         if (e.isSpecialKey()) {
40264             return false;
40265         }
40266         var k = String.fromCharCode(e.getKey()).toUpperCase();
40267         //Roo.log(k);
40268         var match  = false;
40269         var csel = this.view.getSelectedNodes();
40270         var cselitem = false;
40271         if (csel.length) {
40272             var ix = this.view.indexOf(csel[0]);
40273             cselitem  = this.store.getAt(ix);
40274             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40275                 cselitem = false;
40276             }
40277             
40278         }
40279         
40280         this.store.each(function(v) { 
40281             if (cselitem) {
40282                 // start at existing selection.
40283                 if (cselitem.id == v.id) {
40284                     cselitem = false;
40285                 }
40286                 return;
40287             }
40288                 
40289             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40290                 match = this.store.indexOf(v);
40291                 return false;
40292             }
40293         }, this);
40294         
40295         if (match === false) {
40296             return true; // no more action?
40297         }
40298         // scroll to?
40299         this.view.select(match);
40300         var sn = Roo.get(this.view.getSelectedNodes()[0])
40301         sn.scrollIntoView(sn.dom.parentNode, false);
40302     }
40303
40304     /** 
40305     * @cfg {Boolean} grow 
40306     * @hide 
40307     */
40308     /** 
40309     * @cfg {Number} growMin 
40310     * @hide 
40311     */
40312     /** 
40313     * @cfg {Number} growMax 
40314     * @hide 
40315     */
40316     /**
40317      * @hide
40318      * @method autoSize
40319      */
40320 });/*
40321  * Copyright(c) 2010-2012, Roo J Solutions Limited
40322  *
40323  * Licence LGPL
40324  *
40325  */
40326
40327 /**
40328  * @class Roo.form.ComboBoxArray
40329  * @extends Roo.form.TextField
40330  * A facebook style adder... for lists of email / people / countries  etc...
40331  * pick multiple items from a combo box, and shows each one.
40332  *
40333  *  Fred [x]  Brian [x]  [Pick another |v]
40334  *
40335  *
40336  *  For this to work: it needs various extra information
40337  *    - normal combo problay has
40338  *      name, hiddenName
40339  *    + displayField, valueField
40340  *
40341  *    For our purpose...
40342  *
40343  *
40344  *   If we change from 'extends' to wrapping...
40345  *   
40346  *  
40347  *
40348  
40349  
40350  * @constructor
40351  * Create a new ComboBoxArray.
40352  * @param {Object} config Configuration options
40353  */
40354  
40355
40356 Roo.form.ComboBoxArray = function(config)
40357 {
40358     this.addEvents({
40359         /**
40360          * @event beforeremove
40361          * Fires before remove the value from the list
40362              * @param {Roo.form.ComboBoxArray} _self This combo box array
40363              * @param {Roo.form.ComboBoxArray.Item} item removed item
40364              */
40365         'beforeremove' : true,
40366         /**
40367          * @event remove
40368          * Fires when remove the value from the list
40369              * @param {Roo.form.ComboBoxArray} _self This combo box array
40370              * @param {Roo.form.ComboBoxArray.Item} item removed item
40371              */
40372         'remove' : true
40373         
40374         
40375     });
40376     
40377     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40378     
40379     this.items = new Roo.util.MixedCollection(false);
40380     
40381     // construct the child combo...
40382     
40383     
40384     
40385     
40386    
40387     
40388 }
40389
40390  
40391 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40392
40393     /**
40394      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40395      */
40396     
40397     lastData : false,
40398     
40399     // behavies liek a hiddne field
40400     inputType:      'hidden',
40401     /**
40402      * @cfg {Number} width The width of the box that displays the selected element
40403      */ 
40404     width:          300,
40405
40406     
40407     
40408     /**
40409      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40410      */
40411     name : false,
40412     /**
40413      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40414      */
40415     hiddenName : false,
40416     
40417     
40418     // private the array of items that are displayed..
40419     items  : false,
40420     // private - the hidden field el.
40421     hiddenEl : false,
40422     // private - the filed el..
40423     el : false,
40424     
40425     //validateValue : function() { return true; }, // all values are ok!
40426     //onAddClick: function() { },
40427     
40428     onRender : function(ct, position) 
40429     {
40430         
40431         // create the standard hidden element
40432         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40433         
40434         
40435         // give fake names to child combo;
40436         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40437         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40438         
40439         this.combo = Roo.factory(this.combo, Roo.form);
40440         this.combo.onRender(ct, position);
40441         if (typeof(this.combo.width) != 'undefined') {
40442             this.combo.onResize(this.combo.width,0);
40443         }
40444         
40445         this.combo.initEvents();
40446         
40447         // assigned so form know we need to do this..
40448         this.store          = this.combo.store;
40449         this.valueField     = this.combo.valueField;
40450         this.displayField   = this.combo.displayField ;
40451         
40452         
40453         this.combo.wrap.addClass('x-cbarray-grp');
40454         
40455         var cbwrap = this.combo.wrap.createChild(
40456             {tag: 'div', cls: 'x-cbarray-cb'},
40457             this.combo.el.dom
40458         );
40459         
40460              
40461         this.hiddenEl = this.combo.wrap.createChild({
40462             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40463         });
40464         this.el = this.combo.wrap.createChild({
40465             tag: 'input',  type:'hidden' , name: this.name, value : ''
40466         });
40467          //   this.el.dom.removeAttribute("name");
40468         
40469         
40470         this.outerWrap = this.combo.wrap;
40471         this.wrap = cbwrap;
40472         
40473         this.outerWrap.setWidth(this.width);
40474         this.outerWrap.dom.removeChild(this.el.dom);
40475         
40476         this.wrap.dom.appendChild(this.el.dom);
40477         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40478         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40479         
40480         this.combo.trigger.setStyle('position','relative');
40481         this.combo.trigger.setStyle('left', '0px');
40482         this.combo.trigger.setStyle('top', '2px');
40483         
40484         this.combo.el.setStyle('vertical-align', 'text-bottom');
40485         
40486         //this.trigger.setStyle('vertical-align', 'top');
40487         
40488         // this should use the code from combo really... on('add' ....)
40489         if (this.adder) {
40490             
40491         
40492             this.adder = this.outerWrap.createChild(
40493                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40494             var _t = this;
40495             this.adder.on('click', function(e) {
40496                 _t.fireEvent('adderclick', this, e);
40497             }, _t);
40498         }
40499         //var _t = this;
40500         //this.adder.on('click', this.onAddClick, _t);
40501         
40502         
40503         this.combo.on('select', function(cb, rec, ix) {
40504             this.addItem(rec.data);
40505             
40506             cb.setValue('');
40507             cb.el.dom.value = '';
40508             //cb.lastData = rec.data;
40509             // add to list
40510             
40511         }, this);
40512         
40513         
40514     },
40515     
40516     
40517     getName: function()
40518     {
40519         // returns hidden if it's set..
40520         if (!this.rendered) {return ''};
40521         return  this.hiddenName ? this.hiddenName : this.name;
40522         
40523     },
40524     
40525     
40526     onResize: function(w, h){
40527         
40528         return;
40529         // not sure if this is needed..
40530         //this.combo.onResize(w,h);
40531         
40532         if(typeof w != 'number'){
40533             // we do not handle it!?!?
40534             return;
40535         }
40536         var tw = this.combo.trigger.getWidth();
40537         tw += this.addicon ? this.addicon.getWidth() : 0;
40538         tw += this.editicon ? this.editicon.getWidth() : 0;
40539         var x = w - tw;
40540         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40541             
40542         this.combo.trigger.setStyle('left', '0px');
40543         
40544         if(this.list && this.listWidth === undefined){
40545             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40546             this.list.setWidth(lw);
40547             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40548         }
40549         
40550     
40551         
40552     },
40553     
40554     addItem: function(rec)
40555     {
40556         var valueField = this.combo.valueField;
40557         var displayField = this.combo.displayField;
40558         if (this.items.indexOfKey(rec[valueField]) > -1) {
40559             //console.log("GOT " + rec.data.id);
40560             return;
40561         }
40562         
40563         var x = new Roo.form.ComboBoxArray.Item({
40564             //id : rec[this.idField],
40565             data : rec,
40566             displayField : displayField ,
40567             tipField : displayField ,
40568             cb : this
40569         });
40570         // use the 
40571         this.items.add(rec[valueField],x);
40572         // add it before the element..
40573         this.updateHiddenEl();
40574         x.render(this.outerWrap, this.wrap.dom);
40575         // add the image handler..
40576     },
40577     
40578     updateHiddenEl : function()
40579     {
40580         this.validate();
40581         if (!this.hiddenEl) {
40582             return;
40583         }
40584         var ar = [];
40585         var idField = this.combo.valueField;
40586         
40587         this.items.each(function(f) {
40588             ar.push(f.data[idField]);
40589            
40590         });
40591         this.hiddenEl.dom.value = ar.join(',');
40592         this.validate();
40593     },
40594     
40595     reset : function()
40596     {
40597         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40598         this.items.each(function(f) {
40599            f.remove(); 
40600         });
40601         this.el.dom.value = '';
40602         if (this.hiddenEl) {
40603             this.hiddenEl.dom.value = '';
40604         }
40605         
40606     },
40607     getValue: function()
40608     {
40609         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40610     },
40611     setValue: function(v) // not a valid action - must use addItems..
40612     {
40613          
40614         this.reset();
40615         
40616         
40617         
40618         if (this.store.isLocal && (typeof(v) == 'string')) {
40619             // then we can use the store to find the values..
40620             // comma seperated at present.. this needs to allow JSON based encoding..
40621             this.hiddenEl.value  = v;
40622             var v_ar = [];
40623             Roo.each(v.split(','), function(k) {
40624                 Roo.log("CHECK " + this.valueField + ',' + k);
40625                 var li = this.store.query(this.valueField, k);
40626                 if (!li.length) {
40627                     return;
40628                 }
40629                 var add = {};
40630                 add[this.valueField] = k;
40631                 add[this.displayField] = li.item(0).data[this.displayField];
40632                 
40633                 this.addItem(add);
40634             }, this) 
40635              
40636         }
40637         if (typeof(v) == 'object' ) {
40638             // then let's assume it's an array of objects..
40639             Roo.each(v, function(l) {
40640                 this.addItem(l);
40641             }, this);
40642              
40643         }
40644         
40645         
40646     },
40647     setFromData: function(v)
40648     {
40649         // this recieves an object, if setValues is called.
40650         this.reset();
40651         this.el.dom.value = v[this.displayField];
40652         this.hiddenEl.dom.value = v[this.valueField];
40653         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40654             return;
40655         }
40656         var kv = v[this.valueField];
40657         var dv = v[this.displayField];
40658         kv = typeof(kv) != 'string' ? '' : kv;
40659         dv = typeof(dv) != 'string' ? '' : dv;
40660         
40661         
40662         var keys = kv.split(',');
40663         var display = dv.split(',');
40664         for (var i = 0 ; i < keys.length; i++) {
40665             
40666             add = {};
40667             add[this.valueField] = keys[i];
40668             add[this.displayField] = display[i];
40669             this.addItem(add);
40670         }
40671       
40672         
40673     },
40674     
40675     /**
40676      * Validates the combox array value
40677      * @return {Boolean} True if the value is valid, else false
40678      */
40679     validate : function(){
40680         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40681             this.clearInvalid();
40682             return true;
40683         }
40684         return false;
40685     },
40686     
40687     validateValue : function(value){
40688         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40689         
40690     },
40691     
40692     /*@
40693      * overide
40694      * 
40695      */
40696     isDirty : function() {
40697         if(this.disabled) {
40698             return false;
40699         }
40700         
40701         try {
40702             var d = Roo.decode(String(this.originalValue));
40703         } catch (e) {
40704             return String(this.getValue()) !== String(this.originalValue);
40705         }
40706         
40707         var originalValue = [];
40708         
40709         for (var i = 0; i < d.length; i++){
40710             originalValue.push(d[i][this.valueField]);
40711         }
40712         
40713         return String(this.getValue()) !== String(originalValue.join(','));
40714         
40715     }
40716     
40717 });
40718
40719
40720
40721 /**
40722  * @class Roo.form.ComboBoxArray.Item
40723  * @extends Roo.BoxComponent
40724  * A selected item in the list
40725  *  Fred [x]  Brian [x]  [Pick another |v]
40726  * 
40727  * @constructor
40728  * Create a new item.
40729  * @param {Object} config Configuration options
40730  */
40731  
40732 Roo.form.ComboBoxArray.Item = function(config) {
40733     config.id = Roo.id();
40734     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40735 }
40736
40737 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40738     data : {},
40739     cb: false,
40740     displayField : false,
40741     tipField : false,
40742     
40743     
40744     defaultAutoCreate : {
40745         tag: 'div',
40746         cls: 'x-cbarray-item',
40747         cn : [ 
40748             { tag: 'div' },
40749             {
40750                 tag: 'img',
40751                 width:16,
40752                 height : 16,
40753                 src : Roo.BLANK_IMAGE_URL ,
40754                 align: 'center'
40755             }
40756         ]
40757         
40758     },
40759     
40760  
40761     onRender : function(ct, position)
40762     {
40763         Roo.form.Field.superclass.onRender.call(this, ct, position);
40764         
40765         if(!this.el){
40766             var cfg = this.getAutoCreate();
40767             this.el = ct.createChild(cfg, position);
40768         }
40769         
40770         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40771         
40772         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40773             this.cb.renderer(this.data) :
40774             String.format('{0}',this.data[this.displayField]);
40775         
40776             
40777         this.el.child('div').dom.setAttribute('qtip',
40778                         String.format('{0}',this.data[this.tipField])
40779         );
40780         
40781         this.el.child('img').on('click', this.remove, this);
40782         
40783     },
40784    
40785     remove : function()
40786     {
40787         if(this.cb.disabled){
40788             return;
40789         }
40790         
40791         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40792             this.cb.items.remove(this);
40793             this.el.child('img').un('click', this.remove, this);
40794             this.el.remove();
40795             this.cb.updateHiddenEl();
40796
40797             this.cb.fireEvent('remove', this.cb, this);
40798         }
40799         
40800     }
40801 });/*
40802  * Based on:
40803  * Ext JS Library 1.1.1
40804  * Copyright(c) 2006-2007, Ext JS, LLC.
40805  *
40806  * Originally Released Under LGPL - original licence link has changed is not relivant.
40807  *
40808  * Fork - LGPL
40809  * <script type="text/javascript">
40810  */
40811 /**
40812  * @class Roo.form.Checkbox
40813  * @extends Roo.form.Field
40814  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40815  * @constructor
40816  * Creates a new Checkbox
40817  * @param {Object} config Configuration options
40818  */
40819 Roo.form.Checkbox = function(config){
40820     Roo.form.Checkbox.superclass.constructor.call(this, config);
40821     this.addEvents({
40822         /**
40823          * @event check
40824          * Fires when the checkbox is checked or unchecked.
40825              * @param {Roo.form.Checkbox} this This checkbox
40826              * @param {Boolean} checked The new checked value
40827              */
40828         check : true
40829     });
40830 };
40831
40832 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40833     /**
40834      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40835      */
40836     focusClass : undefined,
40837     /**
40838      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40839      */
40840     fieldClass: "x-form-field",
40841     /**
40842      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40843      */
40844     checked: false,
40845     /**
40846      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40847      * {tag: "input", type: "checkbox", autocomplete: "off"})
40848      */
40849     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40850     /**
40851      * @cfg {String} boxLabel The text that appears beside the checkbox
40852      */
40853     boxLabel : "",
40854     /**
40855      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40856      */  
40857     inputValue : '1',
40858     /**
40859      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40860      */
40861      valueOff: '0', // value when not checked..
40862
40863     actionMode : 'viewEl', 
40864     //
40865     // private
40866     itemCls : 'x-menu-check-item x-form-item',
40867     groupClass : 'x-menu-group-item',
40868     inputType : 'hidden',
40869     
40870     
40871     inSetChecked: false, // check that we are not calling self...
40872     
40873     inputElement: false, // real input element?
40874     basedOn: false, // ????
40875     
40876     isFormField: true, // not sure where this is needed!!!!
40877
40878     onResize : function(){
40879         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40880         if(!this.boxLabel){
40881             this.el.alignTo(this.wrap, 'c-c');
40882         }
40883     },
40884
40885     initEvents : function(){
40886         Roo.form.Checkbox.superclass.initEvents.call(this);
40887         this.el.on("click", this.onClick,  this);
40888         this.el.on("change", this.onClick,  this);
40889     },
40890
40891
40892     getResizeEl : function(){
40893         return this.wrap;
40894     },
40895
40896     getPositionEl : function(){
40897         return this.wrap;
40898     },
40899
40900     // private
40901     onRender : function(ct, position){
40902         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40903         /*
40904         if(this.inputValue !== undefined){
40905             this.el.dom.value = this.inputValue;
40906         }
40907         */
40908         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40909         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40910         var viewEl = this.wrap.createChild({ 
40911             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40912         this.viewEl = viewEl;   
40913         this.wrap.on('click', this.onClick,  this); 
40914         
40915         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40916         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40917         
40918         
40919         
40920         if(this.boxLabel){
40921             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40922         //    viewEl.on('click', this.onClick,  this); 
40923         }
40924         //if(this.checked){
40925             this.setChecked(this.checked);
40926         //}else{
40927             //this.checked = this.el.dom;
40928         //}
40929
40930     },
40931
40932     // private
40933     initValue : Roo.emptyFn,
40934
40935     /**
40936      * Returns the checked state of the checkbox.
40937      * @return {Boolean} True if checked, else false
40938      */
40939     getValue : function(){
40940         if(this.el){
40941             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40942         }
40943         return this.valueOff;
40944         
40945     },
40946
40947         // private
40948     onClick : function(){ 
40949         if (this.disabled) {
40950             return;
40951         }
40952         this.setChecked(!this.checked);
40953
40954         //if(this.el.dom.checked != this.checked){
40955         //    this.setValue(this.el.dom.checked);
40956        // }
40957     },
40958
40959     /**
40960      * Sets the checked state of the checkbox.
40961      * On is always based on a string comparison between inputValue and the param.
40962      * @param {Boolean/String} value - the value to set 
40963      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40964      */
40965     setValue : function(v,suppressEvent){
40966         
40967         
40968         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40969         //if(this.el && this.el.dom){
40970         //    this.el.dom.checked = this.checked;
40971         //    this.el.dom.defaultChecked = this.checked;
40972         //}
40973         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40974         //this.fireEvent("check", this, this.checked);
40975     },
40976     // private..
40977     setChecked : function(state,suppressEvent)
40978     {
40979         if (this.inSetChecked) {
40980             this.checked = state;
40981             return;
40982         }
40983         
40984     
40985         if(this.wrap){
40986             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40987         }
40988         this.checked = state;
40989         if(suppressEvent !== true){
40990             this.fireEvent('check', this, state);
40991         }
40992         this.inSetChecked = true;
40993         this.el.dom.value = state ? this.inputValue : this.valueOff;
40994         this.inSetChecked = false;
40995         
40996     },
40997     // handle setting of hidden value by some other method!!?!?
40998     setFromHidden: function()
40999     {
41000         if(!this.el){
41001             return;
41002         }
41003         //console.log("SET FROM HIDDEN");
41004         //alert('setFrom hidden');
41005         this.setValue(this.el.dom.value);
41006     },
41007     
41008     onDestroy : function()
41009     {
41010         if(this.viewEl){
41011             Roo.get(this.viewEl).remove();
41012         }
41013          
41014         Roo.form.Checkbox.superclass.onDestroy.call(this);
41015     }
41016
41017 });/*
41018  * Based on:
41019  * Ext JS Library 1.1.1
41020  * Copyright(c) 2006-2007, Ext JS, LLC.
41021  *
41022  * Originally Released Under LGPL - original licence link has changed is not relivant.
41023  *
41024  * Fork - LGPL
41025  * <script type="text/javascript">
41026  */
41027  
41028 /**
41029  * @class Roo.form.Radio
41030  * @extends Roo.form.Checkbox
41031  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41032  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41033  * @constructor
41034  * Creates a new Radio
41035  * @param {Object} config Configuration options
41036  */
41037 Roo.form.Radio = function(){
41038     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41039 };
41040 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41041     inputType: 'radio',
41042
41043     /**
41044      * If this radio is part of a group, it will return the selected value
41045      * @return {String}
41046      */
41047     getGroupValue : function(){
41048         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41049     },
41050     
41051     
41052     onRender : function(ct, position){
41053         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41054         
41055         if(this.inputValue !== undefined){
41056             this.el.dom.value = this.inputValue;
41057         }
41058          
41059         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41060         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41061         //var viewEl = this.wrap.createChild({ 
41062         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41063         //this.viewEl = viewEl;   
41064         //this.wrap.on('click', this.onClick,  this); 
41065         
41066         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41067         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41068         
41069         
41070         
41071         if(this.boxLabel){
41072             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41073         //    viewEl.on('click', this.onClick,  this); 
41074         }
41075          if(this.checked){
41076             this.el.dom.checked =   'checked' ;
41077         }
41078          
41079     } 
41080     
41081     
41082 });//<script type="text/javascript">
41083
41084 /*
41085  * Based  Ext JS Library 1.1.1
41086  * Copyright(c) 2006-2007, Ext JS, LLC.
41087  * LGPL
41088  *
41089  */
41090  
41091 /**
41092  * @class Roo.HtmlEditorCore
41093  * @extends Roo.Component
41094  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41095  *
41096  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41097  */
41098
41099 Roo.HtmlEditorCore = function(config){
41100     
41101     
41102     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41103     
41104     
41105     this.addEvents({
41106         /**
41107          * @event initialize
41108          * Fires when the editor is fully initialized (including the iframe)
41109          * @param {Roo.HtmlEditorCore} this
41110          */
41111         initialize: true,
41112         /**
41113          * @event activate
41114          * Fires when the editor is first receives the focus. Any insertion must wait
41115          * until after this event.
41116          * @param {Roo.HtmlEditorCore} this
41117          */
41118         activate: true,
41119          /**
41120          * @event beforesync
41121          * Fires before the textarea is updated with content from the editor iframe. Return false
41122          * to cancel the sync.
41123          * @param {Roo.HtmlEditorCore} this
41124          * @param {String} html
41125          */
41126         beforesync: true,
41127          /**
41128          * @event beforepush
41129          * Fires before the iframe editor is updated with content from the textarea. Return false
41130          * to cancel the push.
41131          * @param {Roo.HtmlEditorCore} this
41132          * @param {String} html
41133          */
41134         beforepush: true,
41135          /**
41136          * @event sync
41137          * Fires when the textarea is updated with content from the editor iframe.
41138          * @param {Roo.HtmlEditorCore} this
41139          * @param {String} html
41140          */
41141         sync: true,
41142          /**
41143          * @event push
41144          * Fires when the iframe editor is updated with content from the textarea.
41145          * @param {Roo.HtmlEditorCore} this
41146          * @param {String} html
41147          */
41148         push: true,
41149         
41150         /**
41151          * @event editorevent
41152          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41153          * @param {Roo.HtmlEditorCore} this
41154          */
41155         editorevent: true
41156         
41157     });
41158     
41159     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41160     
41161     // defaults : white / black...
41162     this.applyBlacklists();
41163     
41164     
41165     
41166 };
41167
41168
41169 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41170
41171
41172      /**
41173      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41174      */
41175     
41176     owner : false,
41177     
41178      /**
41179      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41180      *                        Roo.resizable.
41181      */
41182     resizable : false,
41183      /**
41184      * @cfg {Number} height (in pixels)
41185      */   
41186     height: 300,
41187    /**
41188      * @cfg {Number} width (in pixels)
41189      */   
41190     width: 500,
41191     
41192     /**
41193      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41194      * 
41195      */
41196     stylesheets: false,
41197     
41198     // id of frame..
41199     frameId: false,
41200     
41201     // private properties
41202     validationEvent : false,
41203     deferHeight: true,
41204     initialized : false,
41205     activated : false,
41206     sourceEditMode : false,
41207     onFocus : Roo.emptyFn,
41208     iframePad:3,
41209     hideMode:'offsets',
41210     
41211     clearUp: true,
41212     
41213     // blacklist + whitelisted elements..
41214     black: false,
41215     white: false,
41216      
41217     
41218
41219     /**
41220      * Protected method that will not generally be called directly. It
41221      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41222      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41223      */
41224     getDocMarkup : function(){
41225         // body styles..
41226         var st = '';
41227         
41228         // inherit styels from page...?? 
41229         if (this.stylesheets === false) {
41230             
41231             Roo.get(document.head).select('style').each(function(node) {
41232                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41233             });
41234             
41235             Roo.get(document.head).select('link').each(function(node) { 
41236                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41237             });
41238             
41239         } else if (!this.stylesheets.length) {
41240                 // simple..
41241                 st = '<style type="text/css">' +
41242                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41243                    '</style>';
41244         } else { 
41245             
41246         }
41247         
41248         st +=  '<style type="text/css">' +
41249             'IMG { cursor: pointer } ' +
41250         '</style>';
41251
41252         
41253         return '<html><head>' + st  +
41254             //<style type="text/css">' +
41255             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41256             //'</style>' +
41257             ' </head><body class="roo-htmleditor-body"></body></html>';
41258     },
41259
41260     // private
41261     onRender : function(ct, position)
41262     {
41263         var _t = this;
41264         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41265         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41266         
41267         
41268         this.el.dom.style.border = '0 none';
41269         this.el.dom.setAttribute('tabIndex', -1);
41270         this.el.addClass('x-hidden hide');
41271         
41272         
41273         
41274         if(Roo.isIE){ // fix IE 1px bogus margin
41275             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41276         }
41277        
41278         
41279         this.frameId = Roo.id();
41280         
41281          
41282         
41283         var iframe = this.owner.wrap.createChild({
41284             tag: 'iframe',
41285             cls: 'form-control', // bootstrap..
41286             id: this.frameId,
41287             name: this.frameId,
41288             frameBorder : 'no',
41289             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41290         }, this.el
41291         );
41292         
41293         
41294         this.iframe = iframe.dom;
41295
41296          this.assignDocWin();
41297         
41298         this.doc.designMode = 'on';
41299        
41300         this.doc.open();
41301         this.doc.write(this.getDocMarkup());
41302         this.doc.close();
41303
41304         
41305         var task = { // must defer to wait for browser to be ready
41306             run : function(){
41307                 //console.log("run task?" + this.doc.readyState);
41308                 this.assignDocWin();
41309                 if(this.doc.body || this.doc.readyState == 'complete'){
41310                     try {
41311                         this.doc.designMode="on";
41312                     } catch (e) {
41313                         return;
41314                     }
41315                     Roo.TaskMgr.stop(task);
41316                     this.initEditor.defer(10, this);
41317                 }
41318             },
41319             interval : 10,
41320             duration: 10000,
41321             scope: this
41322         };
41323         Roo.TaskMgr.start(task);
41324
41325     },
41326
41327     // private
41328     onResize : function(w, h)
41329     {
41330          Roo.log('resize: ' +w + ',' + h );
41331         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41332         if(!this.iframe){
41333             return;
41334         }
41335         if(typeof w == 'number'){
41336             
41337             this.iframe.style.width = w + 'px';
41338         }
41339         if(typeof h == 'number'){
41340             
41341             this.iframe.style.height = h + 'px';
41342             if(this.doc){
41343                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41344             }
41345         }
41346         
41347     },
41348
41349     /**
41350      * Toggles the editor between standard and source edit mode.
41351      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41352      */
41353     toggleSourceEdit : function(sourceEditMode){
41354         
41355         this.sourceEditMode = sourceEditMode === true;
41356         
41357         if(this.sourceEditMode){
41358  
41359             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41360             
41361         }else{
41362             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41363             //this.iframe.className = '';
41364             this.deferFocus();
41365         }
41366         //this.setSize(this.owner.wrap.getSize());
41367         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41368     },
41369
41370     
41371   
41372
41373     /**
41374      * Protected method that will not generally be called directly. If you need/want
41375      * custom HTML cleanup, this is the method you should override.
41376      * @param {String} html The HTML to be cleaned
41377      * return {String} The cleaned HTML
41378      */
41379     cleanHtml : function(html){
41380         html = String(html);
41381         if(html.length > 5){
41382             if(Roo.isSafari){ // strip safari nonsense
41383                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41384             }
41385         }
41386         if(html == '&nbsp;'){
41387             html = '';
41388         }
41389         return html;
41390     },
41391
41392     /**
41393      * HTML Editor -> Textarea
41394      * Protected method that will not generally be called directly. Syncs the contents
41395      * of the editor iframe with the textarea.
41396      */
41397     syncValue : function(){
41398         if(this.initialized){
41399             var bd = (this.doc.body || this.doc.documentElement);
41400             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41401             var html = bd.innerHTML;
41402             if(Roo.isSafari){
41403                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41404                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41405                 if(m && m[1]){
41406                     html = '<div style="'+m[0]+'">' + html + '</div>';
41407                 }
41408             }
41409             html = this.cleanHtml(html);
41410             // fix up the special chars.. normaly like back quotes in word...
41411             // however we do not want to do this with chinese..
41412             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41413                 var cc = b.charCodeAt();
41414                 if (
41415                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41416                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41417                     (cc >= 0xf900 && cc < 0xfb00 )
41418                 ) {
41419                         return b;
41420                 }
41421                 return "&#"+cc+";" 
41422             });
41423             if(this.owner.fireEvent('beforesync', this, html) !== false){
41424                 this.el.dom.value = html;
41425                 this.owner.fireEvent('sync', this, html);
41426             }
41427         }
41428     },
41429
41430     /**
41431      * Protected method that will not generally be called directly. Pushes the value of the textarea
41432      * into the iframe editor.
41433      */
41434     pushValue : function(){
41435         if(this.initialized){
41436             var v = this.el.dom.value.trim();
41437             
41438 //            if(v.length < 1){
41439 //                v = '&#160;';
41440 //            }
41441             
41442             if(this.owner.fireEvent('beforepush', this, v) !== false){
41443                 var d = (this.doc.body || this.doc.documentElement);
41444                 d.innerHTML = v;
41445                 this.cleanUpPaste();
41446                 this.el.dom.value = d.innerHTML;
41447                 this.owner.fireEvent('push', this, v);
41448             }
41449         }
41450     },
41451
41452     // private
41453     deferFocus : function(){
41454         this.focus.defer(10, this);
41455     },
41456
41457     // doc'ed in Field
41458     focus : function(){
41459         if(this.win && !this.sourceEditMode){
41460             this.win.focus();
41461         }else{
41462             this.el.focus();
41463         }
41464     },
41465     
41466     assignDocWin: function()
41467     {
41468         var iframe = this.iframe;
41469         
41470          if(Roo.isIE){
41471             this.doc = iframe.contentWindow.document;
41472             this.win = iframe.contentWindow;
41473         } else {
41474 //            if (!Roo.get(this.frameId)) {
41475 //                return;
41476 //            }
41477 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41478 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41479             
41480             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41481                 return;
41482             }
41483             
41484             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41485             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41486         }
41487     },
41488     
41489     // private
41490     initEditor : function(){
41491         //console.log("INIT EDITOR");
41492         this.assignDocWin();
41493         
41494         
41495         
41496         this.doc.designMode="on";
41497         this.doc.open();
41498         this.doc.write(this.getDocMarkup());
41499         this.doc.close();
41500         
41501         var dbody = (this.doc.body || this.doc.documentElement);
41502         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41503         // this copies styles from the containing element into thsi one..
41504         // not sure why we need all of this..
41505         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41506         
41507         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41508         //ss['background-attachment'] = 'fixed'; // w3c
41509         dbody.bgProperties = 'fixed'; // ie
41510         //Roo.DomHelper.applyStyles(dbody, ss);
41511         Roo.EventManager.on(this.doc, {
41512             //'mousedown': this.onEditorEvent,
41513             'mouseup': this.onEditorEvent,
41514             'dblclick': this.onEditorEvent,
41515             'click': this.onEditorEvent,
41516             'keyup': this.onEditorEvent,
41517             buffer:100,
41518             scope: this
41519         });
41520         if(Roo.isGecko){
41521             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41522         }
41523         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41524             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41525         }
41526         this.initialized = true;
41527
41528         this.owner.fireEvent('initialize', this);
41529         this.pushValue();
41530     },
41531
41532     // private
41533     onDestroy : function(){
41534         
41535         
41536         
41537         if(this.rendered){
41538             
41539             //for (var i =0; i < this.toolbars.length;i++) {
41540             //    // fixme - ask toolbars for heights?
41541             //    this.toolbars[i].onDestroy();
41542            // }
41543             
41544             //this.wrap.dom.innerHTML = '';
41545             //this.wrap.remove();
41546         }
41547     },
41548
41549     // private
41550     onFirstFocus : function(){
41551         
41552         this.assignDocWin();
41553         
41554         
41555         this.activated = true;
41556          
41557     
41558         if(Roo.isGecko){ // prevent silly gecko errors
41559             this.win.focus();
41560             var s = this.win.getSelection();
41561             if(!s.focusNode || s.focusNode.nodeType != 3){
41562                 var r = s.getRangeAt(0);
41563                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41564                 r.collapse(true);
41565                 this.deferFocus();
41566             }
41567             try{
41568                 this.execCmd('useCSS', true);
41569                 this.execCmd('styleWithCSS', false);
41570             }catch(e){}
41571         }
41572         this.owner.fireEvent('activate', this);
41573     },
41574
41575     // private
41576     adjustFont: function(btn){
41577         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41578         //if(Roo.isSafari){ // safari
41579         //    adjust *= 2;
41580        // }
41581         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41582         if(Roo.isSafari){ // safari
41583             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41584             v =  (v < 10) ? 10 : v;
41585             v =  (v > 48) ? 48 : v;
41586             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41587             
41588         }
41589         
41590         
41591         v = Math.max(1, v+adjust);
41592         
41593         this.execCmd('FontSize', v  );
41594     },
41595
41596     onEditorEvent : function(e){
41597         this.owner.fireEvent('editorevent', this, e);
41598       //  this.updateToolbar();
41599         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41600     },
41601
41602     insertTag : function(tg)
41603     {
41604         // could be a bit smarter... -> wrap the current selected tRoo..
41605         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41606             
41607             range = this.createRange(this.getSelection());
41608             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41609             wrappingNode.appendChild(range.extractContents());
41610             range.insertNode(wrappingNode);
41611
41612             return;
41613             
41614             
41615             
41616         }
41617         this.execCmd("formatblock",   tg);
41618         
41619     },
41620     
41621     insertText : function(txt)
41622     {
41623         
41624         
41625         var range = this.createRange();
41626         range.deleteContents();
41627                //alert(Sender.getAttribute('label'));
41628                
41629         range.insertNode(this.doc.createTextNode(txt));
41630     } ,
41631     
41632      
41633
41634     /**
41635      * Executes a Midas editor command on the editor document and performs necessary focus and
41636      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41637      * @param {String} cmd The Midas command
41638      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41639      */
41640     relayCmd : function(cmd, value){
41641         this.win.focus();
41642         this.execCmd(cmd, value);
41643         this.owner.fireEvent('editorevent', this);
41644         //this.updateToolbar();
41645         this.owner.deferFocus();
41646     },
41647
41648     /**
41649      * Executes a Midas editor command directly on the editor document.
41650      * For visual commands, you should use {@link #relayCmd} instead.
41651      * <b>This should only be called after the editor is initialized.</b>
41652      * @param {String} cmd The Midas command
41653      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41654      */
41655     execCmd : function(cmd, value){
41656         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41657         this.syncValue();
41658     },
41659  
41660  
41661    
41662     /**
41663      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41664      * to insert tRoo.
41665      * @param {String} text | dom node.. 
41666      */
41667     insertAtCursor : function(text)
41668     {
41669         
41670         
41671         
41672         if(!this.activated){
41673             return;
41674         }
41675         /*
41676         if(Roo.isIE){
41677             this.win.focus();
41678             var r = this.doc.selection.createRange();
41679             if(r){
41680                 r.collapse(true);
41681                 r.pasteHTML(text);
41682                 this.syncValue();
41683                 this.deferFocus();
41684             
41685             }
41686             return;
41687         }
41688         */
41689         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41690             this.win.focus();
41691             
41692             
41693             // from jquery ui (MIT licenced)
41694             var range, node;
41695             var win = this.win;
41696             
41697             if (win.getSelection && win.getSelection().getRangeAt) {
41698                 range = win.getSelection().getRangeAt(0);
41699                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41700                 range.insertNode(node);
41701             } else if (win.document.selection && win.document.selection.createRange) {
41702                 // no firefox support
41703                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41704                 win.document.selection.createRange().pasteHTML(txt);
41705             } else {
41706                 // no firefox support
41707                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41708                 this.execCmd('InsertHTML', txt);
41709             } 
41710             
41711             this.syncValue();
41712             
41713             this.deferFocus();
41714         }
41715     },
41716  // private
41717     mozKeyPress : function(e){
41718         if(e.ctrlKey){
41719             var c = e.getCharCode(), cmd;
41720           
41721             if(c > 0){
41722                 c = String.fromCharCode(c).toLowerCase();
41723                 switch(c){
41724                     case 'b':
41725                         cmd = 'bold';
41726                         break;
41727                     case 'i':
41728                         cmd = 'italic';
41729                         break;
41730                     
41731                     case 'u':
41732                         cmd = 'underline';
41733                         break;
41734                     
41735                     case 'v':
41736                         this.cleanUpPaste.defer(100, this);
41737                         return;
41738                         
41739                 }
41740                 if(cmd){
41741                     this.win.focus();
41742                     this.execCmd(cmd);
41743                     this.deferFocus();
41744                     e.preventDefault();
41745                 }
41746                 
41747             }
41748         }
41749     },
41750
41751     // private
41752     fixKeys : function(){ // load time branching for fastest keydown performance
41753         if(Roo.isIE){
41754             return function(e){
41755                 var k = e.getKey(), r;
41756                 if(k == e.TAB){
41757                     e.stopEvent();
41758                     r = this.doc.selection.createRange();
41759                     if(r){
41760                         r.collapse(true);
41761                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41762                         this.deferFocus();
41763                     }
41764                     return;
41765                 }
41766                 
41767                 if(k == e.ENTER){
41768                     r = this.doc.selection.createRange();
41769                     if(r){
41770                         var target = r.parentElement();
41771                         if(!target || target.tagName.toLowerCase() != 'li'){
41772                             e.stopEvent();
41773                             r.pasteHTML('<br />');
41774                             r.collapse(false);
41775                             r.select();
41776                         }
41777                     }
41778                 }
41779                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41780                     this.cleanUpPaste.defer(100, this);
41781                     return;
41782                 }
41783                 
41784                 
41785             };
41786         }else if(Roo.isOpera){
41787             return function(e){
41788                 var k = e.getKey();
41789                 if(k == e.TAB){
41790                     e.stopEvent();
41791                     this.win.focus();
41792                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41793                     this.deferFocus();
41794                 }
41795                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41796                     this.cleanUpPaste.defer(100, this);
41797                     return;
41798                 }
41799                 
41800             };
41801         }else if(Roo.isSafari){
41802             return function(e){
41803                 var k = e.getKey();
41804                 
41805                 if(k == e.TAB){
41806                     e.stopEvent();
41807                     this.execCmd('InsertText','\t');
41808                     this.deferFocus();
41809                     return;
41810                 }
41811                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41812                     this.cleanUpPaste.defer(100, this);
41813                     return;
41814                 }
41815                 
41816              };
41817         }
41818     }(),
41819     
41820     getAllAncestors: function()
41821     {
41822         var p = this.getSelectedNode();
41823         var a = [];
41824         if (!p) {
41825             a.push(p); // push blank onto stack..
41826             p = this.getParentElement();
41827         }
41828         
41829         
41830         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41831             a.push(p);
41832             p = p.parentNode;
41833         }
41834         a.push(this.doc.body);
41835         return a;
41836     },
41837     lastSel : false,
41838     lastSelNode : false,
41839     
41840     
41841     getSelection : function() 
41842     {
41843         this.assignDocWin();
41844         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41845     },
41846     
41847     getSelectedNode: function() 
41848     {
41849         // this may only work on Gecko!!!
41850         
41851         // should we cache this!!!!
41852         
41853         
41854         
41855          
41856         var range = this.createRange(this.getSelection()).cloneRange();
41857         
41858         if (Roo.isIE) {
41859             var parent = range.parentElement();
41860             while (true) {
41861                 var testRange = range.duplicate();
41862                 testRange.moveToElementText(parent);
41863                 if (testRange.inRange(range)) {
41864                     break;
41865                 }
41866                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41867                     break;
41868                 }
41869                 parent = parent.parentElement;
41870             }
41871             return parent;
41872         }
41873         
41874         // is ancestor a text element.
41875         var ac =  range.commonAncestorContainer;
41876         if (ac.nodeType == 3) {
41877             ac = ac.parentNode;
41878         }
41879         
41880         var ar = ac.childNodes;
41881          
41882         var nodes = [];
41883         var other_nodes = [];
41884         var has_other_nodes = false;
41885         for (var i=0;i<ar.length;i++) {
41886             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41887                 continue;
41888             }
41889             // fullly contained node.
41890             
41891             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41892                 nodes.push(ar[i]);
41893                 continue;
41894             }
41895             
41896             // probably selected..
41897             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41898                 other_nodes.push(ar[i]);
41899                 continue;
41900             }
41901             // outer..
41902             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41903                 continue;
41904             }
41905             
41906             
41907             has_other_nodes = true;
41908         }
41909         if (!nodes.length && other_nodes.length) {
41910             nodes= other_nodes;
41911         }
41912         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41913             return false;
41914         }
41915         
41916         return nodes[0];
41917     },
41918     createRange: function(sel)
41919     {
41920         // this has strange effects when using with 
41921         // top toolbar - not sure if it's a great idea.
41922         //this.editor.contentWindow.focus();
41923         if (typeof sel != "undefined") {
41924             try {
41925                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41926             } catch(e) {
41927                 return this.doc.createRange();
41928             }
41929         } else {
41930             return this.doc.createRange();
41931         }
41932     },
41933     getParentElement: function()
41934     {
41935         
41936         this.assignDocWin();
41937         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41938         
41939         var range = this.createRange(sel);
41940          
41941         try {
41942             var p = range.commonAncestorContainer;
41943             while (p.nodeType == 3) { // text node
41944                 p = p.parentNode;
41945             }
41946             return p;
41947         } catch (e) {
41948             return null;
41949         }
41950     
41951     },
41952     /***
41953      *
41954      * Range intersection.. the hard stuff...
41955      *  '-1' = before
41956      *  '0' = hits..
41957      *  '1' = after.
41958      *         [ -- selected range --- ]
41959      *   [fail]                        [fail]
41960      *
41961      *    basically..
41962      *      if end is before start or  hits it. fail.
41963      *      if start is after end or hits it fail.
41964      *
41965      *   if either hits (but other is outside. - then it's not 
41966      *   
41967      *    
41968      **/
41969     
41970     
41971     // @see http://www.thismuchiknow.co.uk/?p=64.
41972     rangeIntersectsNode : function(range, node)
41973     {
41974         var nodeRange = node.ownerDocument.createRange();
41975         try {
41976             nodeRange.selectNode(node);
41977         } catch (e) {
41978             nodeRange.selectNodeContents(node);
41979         }
41980     
41981         var rangeStartRange = range.cloneRange();
41982         rangeStartRange.collapse(true);
41983     
41984         var rangeEndRange = range.cloneRange();
41985         rangeEndRange.collapse(false);
41986     
41987         var nodeStartRange = nodeRange.cloneRange();
41988         nodeStartRange.collapse(true);
41989     
41990         var nodeEndRange = nodeRange.cloneRange();
41991         nodeEndRange.collapse(false);
41992     
41993         return rangeStartRange.compareBoundaryPoints(
41994                  Range.START_TO_START, nodeEndRange) == -1 &&
41995                rangeEndRange.compareBoundaryPoints(
41996                  Range.START_TO_START, nodeStartRange) == 1;
41997         
41998          
41999     },
42000     rangeCompareNode : function(range, node)
42001     {
42002         var nodeRange = node.ownerDocument.createRange();
42003         try {
42004             nodeRange.selectNode(node);
42005         } catch (e) {
42006             nodeRange.selectNodeContents(node);
42007         }
42008         
42009         
42010         range.collapse(true);
42011     
42012         nodeRange.collapse(true);
42013      
42014         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42015         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42016          
42017         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42018         
42019         var nodeIsBefore   =  ss == 1;
42020         var nodeIsAfter    = ee == -1;
42021         
42022         if (nodeIsBefore && nodeIsAfter)
42023             return 0; // outer
42024         if (!nodeIsBefore && nodeIsAfter)
42025             return 1; //right trailed.
42026         
42027         if (nodeIsBefore && !nodeIsAfter)
42028             return 2;  // left trailed.
42029         // fully contined.
42030         return 3;
42031     },
42032
42033     // private? - in a new class?
42034     cleanUpPaste :  function()
42035     {
42036         // cleans up the whole document..
42037         Roo.log('cleanuppaste');
42038         
42039         this.cleanUpChildren(this.doc.body);
42040         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42041         if (clean != this.doc.body.innerHTML) {
42042             this.doc.body.innerHTML = clean;
42043         }
42044         
42045     },
42046     
42047     cleanWordChars : function(input) {// change the chars to hex code
42048         var he = Roo.HtmlEditorCore;
42049         
42050         var output = input;
42051         Roo.each(he.swapCodes, function(sw) { 
42052             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42053             
42054             output = output.replace(swapper, sw[1]);
42055         });
42056         
42057         return output;
42058     },
42059     
42060     
42061     cleanUpChildren : function (n)
42062     {
42063         if (!n.childNodes.length) {
42064             return;
42065         }
42066         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42067            this.cleanUpChild(n.childNodes[i]);
42068         }
42069     },
42070     
42071     
42072         
42073     
42074     cleanUpChild : function (node)
42075     {
42076         var ed = this;
42077         //console.log(node);
42078         if (node.nodeName == "#text") {
42079             // clean up silly Windows -- stuff?
42080             return; 
42081         }
42082         if (node.nodeName == "#comment") {
42083             node.parentNode.removeChild(node);
42084             // clean up silly Windows -- stuff?
42085             return; 
42086         }
42087         var lcname = node.tagName.toLowerCase();
42088         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42089         // whitelist of tags..
42090         
42091         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42092             // remove node.
42093             node.parentNode.removeChild(node);
42094             return;
42095             
42096         }
42097         
42098         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42099         
42100         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42101         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42102         
42103         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42104         //    remove_keep_children = true;
42105         //}
42106         
42107         if (remove_keep_children) {
42108             this.cleanUpChildren(node);
42109             // inserts everything just before this node...
42110             while (node.childNodes.length) {
42111                 var cn = node.childNodes[0];
42112                 node.removeChild(cn);
42113                 node.parentNode.insertBefore(cn, node);
42114             }
42115             node.parentNode.removeChild(node);
42116             return;
42117         }
42118         
42119         if (!node.attributes || !node.attributes.length) {
42120             this.cleanUpChildren(node);
42121             return;
42122         }
42123         
42124         function cleanAttr(n,v)
42125         {
42126             
42127             if (v.match(/^\./) || v.match(/^\//)) {
42128                 return;
42129             }
42130             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42131                 return;
42132             }
42133             if (v.match(/^#/)) {
42134                 return;
42135             }
42136 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42137             node.removeAttribute(n);
42138             
42139         }
42140         
42141         var cwhite = this.cwhite;
42142         var cblack = this.cblack;
42143             
42144         function cleanStyle(n,v)
42145         {
42146             if (v.match(/expression/)) { //XSS?? should we even bother..
42147                 node.removeAttribute(n);
42148                 return;
42149             }
42150             
42151             var parts = v.split(/;/);
42152             var clean = [];
42153             
42154             Roo.each(parts, function(p) {
42155                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42156                 if (!p.length) {
42157                     return true;
42158                 }
42159                 var l = p.split(':').shift().replace(/\s+/g,'');
42160                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42161                 
42162                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42163 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42164                     //node.removeAttribute(n);
42165                     return true;
42166                 }
42167                 //Roo.log()
42168                 // only allow 'c whitelisted system attributes'
42169                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42170 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42171                     //node.removeAttribute(n);
42172                     return true;
42173                 }
42174                 
42175                 
42176                  
42177                 
42178                 clean.push(p);
42179                 return true;
42180             });
42181             if (clean.length) { 
42182                 node.setAttribute(n, clean.join(';'));
42183             } else {
42184                 node.removeAttribute(n);
42185             }
42186             
42187         }
42188         
42189         
42190         for (var i = node.attributes.length-1; i > -1 ; i--) {
42191             var a = node.attributes[i];
42192             //console.log(a);
42193             
42194             if (a.name.toLowerCase().substr(0,2)=='on')  {
42195                 node.removeAttribute(a.name);
42196                 continue;
42197             }
42198             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42199                 node.removeAttribute(a.name);
42200                 continue;
42201             }
42202             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42203                 cleanAttr(a.name,a.value); // fixme..
42204                 continue;
42205             }
42206             if (a.name == 'style') {
42207                 cleanStyle(a.name,a.value);
42208                 continue;
42209             }
42210             /// clean up MS crap..
42211             // tecnically this should be a list of valid class'es..
42212             
42213             
42214             if (a.name == 'class') {
42215                 if (a.value.match(/^Mso/)) {
42216                     node.className = '';
42217                 }
42218                 
42219                 if (a.value.match(/body/)) {
42220                     node.className = '';
42221                 }
42222                 continue;
42223             }
42224             
42225             // style cleanup!?
42226             // class cleanup?
42227             
42228         }
42229         
42230         
42231         this.cleanUpChildren(node);
42232         
42233         
42234     },
42235     /**
42236      * Clean up MS wordisms...
42237      */
42238     cleanWord : function(node)
42239     {
42240         var _t = this;
42241         var cleanWordChildren = function()
42242         {
42243             if (!node.childNodes.length) {
42244                 return;
42245             }
42246             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42247                _t.cleanWord(node.childNodes[i]);
42248             }
42249         }
42250         
42251         
42252         if (!node) {
42253             this.cleanWord(this.doc.body);
42254             return;
42255         }
42256         if (node.nodeName == "#text") {
42257             // clean up silly Windows -- stuff?
42258             return; 
42259         }
42260         if (node.nodeName == "#comment") {
42261             node.parentNode.removeChild(node);
42262             // clean up silly Windows -- stuff?
42263             return; 
42264         }
42265         
42266         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42267             node.parentNode.removeChild(node);
42268             return;
42269         }
42270         
42271         // remove - but keep children..
42272         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42273             while (node.childNodes.length) {
42274                 var cn = node.childNodes[0];
42275                 node.removeChild(cn);
42276                 node.parentNode.insertBefore(cn, node);
42277             }
42278             node.parentNode.removeChild(node);
42279             cleanWordChildren();
42280             return;
42281         }
42282         // clean styles
42283         if (node.className.length) {
42284             
42285             var cn = node.className.split(/\W+/);
42286             var cna = [];
42287             Roo.each(cn, function(cls) {
42288                 if (cls.match(/Mso[a-zA-Z]+/)) {
42289                     return;
42290                 }
42291                 cna.push(cls);
42292             });
42293             node.className = cna.length ? cna.join(' ') : '';
42294             if (!cna.length) {
42295                 node.removeAttribute("class");
42296             }
42297         }
42298         
42299         if (node.hasAttribute("lang")) {
42300             node.removeAttribute("lang");
42301         }
42302         
42303         if (node.hasAttribute("style")) {
42304             
42305             var styles = node.getAttribute("style").split(";");
42306             var nstyle = [];
42307             Roo.each(styles, function(s) {
42308                 if (!s.match(/:/)) {
42309                     return;
42310                 }
42311                 var kv = s.split(":");
42312                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42313                     return;
42314                 }
42315                 // what ever is left... we allow.
42316                 nstyle.push(s);
42317             });
42318             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42319             if (!nstyle.length) {
42320                 node.removeAttribute('style');
42321             }
42322         }
42323         
42324         cleanWordChildren();
42325         
42326         
42327     },
42328     domToHTML : function(currentElement, depth, nopadtext) {
42329         
42330         depth = depth || 0;
42331         nopadtext = nopadtext || false;
42332     
42333         if (!currentElement) {
42334             return this.domToHTML(this.doc.body);
42335         }
42336         
42337         //Roo.log(currentElement);
42338         var j;
42339         var allText = false;
42340         var nodeName = currentElement.nodeName;
42341         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42342         
42343         if  (nodeName == '#text') {
42344             
42345             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42346         }
42347         
42348         
42349         var ret = '';
42350         if (nodeName != 'BODY') {
42351              
42352             var i = 0;
42353             // Prints the node tagName, such as <A>, <IMG>, etc
42354             if (tagName) {
42355                 var attr = [];
42356                 for(i = 0; i < currentElement.attributes.length;i++) {
42357                     // quoting?
42358                     var aname = currentElement.attributes.item(i).name;
42359                     if (!currentElement.attributes.item(i).value.length) {
42360                         continue;
42361                     }
42362                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42363                 }
42364                 
42365                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42366             } 
42367             else {
42368                 
42369                 // eack
42370             }
42371         } else {
42372             tagName = false;
42373         }
42374         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42375             return ret;
42376         }
42377         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42378             nopadtext = true;
42379         }
42380         
42381         
42382         // Traverse the tree
42383         i = 0;
42384         var currentElementChild = currentElement.childNodes.item(i);
42385         var allText = true;
42386         var innerHTML  = '';
42387         lastnode = '';
42388         while (currentElementChild) {
42389             // Formatting code (indent the tree so it looks nice on the screen)
42390             var nopad = nopadtext;
42391             if (lastnode == 'SPAN') {
42392                 nopad  = true;
42393             }
42394             // text
42395             if  (currentElementChild.nodeName == '#text') {
42396                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42397                 toadd = nopadtext ? toadd : toadd.trim();
42398                 if (!nopad && toadd.length > 80) {
42399                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42400                 }
42401                 innerHTML  += toadd;
42402                 
42403                 i++;
42404                 currentElementChild = currentElement.childNodes.item(i);
42405                 lastNode = '';
42406                 continue;
42407             }
42408             allText = false;
42409             
42410             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42411                 
42412             // Recursively traverse the tree structure of the child node
42413             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42414             lastnode = currentElementChild.nodeName;
42415             i++;
42416             currentElementChild=currentElement.childNodes.item(i);
42417         }
42418         
42419         ret += innerHTML;
42420         
42421         if (!allText) {
42422                 // The remaining code is mostly for formatting the tree
42423             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42424         }
42425         
42426         
42427         if (tagName) {
42428             ret+= "</"+tagName+">";
42429         }
42430         return ret;
42431         
42432     },
42433         
42434     applyBlacklists : function()
42435     {
42436         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42437         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42438         
42439         this.white = [];
42440         this.black = [];
42441         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42442             if (b.indexOf(tag) > -1) {
42443                 return;
42444             }
42445             this.white.push(tag);
42446             
42447         }, this);
42448         
42449         Roo.each(w, function(tag) {
42450             if (b.indexOf(tag) > -1) {
42451                 return;
42452             }
42453             if (this.white.indexOf(tag) > -1) {
42454                 return;
42455             }
42456             this.white.push(tag);
42457             
42458         }, this);
42459         
42460         
42461         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42462             if (w.indexOf(tag) > -1) {
42463                 return;
42464             }
42465             this.black.push(tag);
42466             
42467         }, this);
42468         
42469         Roo.each(b, function(tag) {
42470             if (w.indexOf(tag) > -1) {
42471                 return;
42472             }
42473             if (this.black.indexOf(tag) > -1) {
42474                 return;
42475             }
42476             this.black.push(tag);
42477             
42478         }, this);
42479         
42480         
42481         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42482         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42483         
42484         this.cwhite = [];
42485         this.cblack = [];
42486         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42487             if (b.indexOf(tag) > -1) {
42488                 return;
42489             }
42490             this.cwhite.push(tag);
42491             
42492         }, this);
42493         
42494         Roo.each(w, function(tag) {
42495             if (b.indexOf(tag) > -1) {
42496                 return;
42497             }
42498             if (this.cwhite.indexOf(tag) > -1) {
42499                 return;
42500             }
42501             this.cwhite.push(tag);
42502             
42503         }, this);
42504         
42505         
42506         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42507             if (w.indexOf(tag) > -1) {
42508                 return;
42509             }
42510             this.cblack.push(tag);
42511             
42512         }, this);
42513         
42514         Roo.each(b, function(tag) {
42515             if (w.indexOf(tag) > -1) {
42516                 return;
42517             }
42518             if (this.cblack.indexOf(tag) > -1) {
42519                 return;
42520             }
42521             this.cblack.push(tag);
42522             
42523         }, this);
42524     },
42525     
42526     setStylesheets : function(stylesheets)
42527     {
42528         if(typeof(stylesheets) == 'string'){
42529             Roo.get(this.iframe.contentDocument.head).createChild({
42530                 tag : 'link',
42531                 rel : 'stylesheet',
42532                 type : 'text/css',
42533                 href : stylesheets
42534             });
42535             
42536             return;
42537         }
42538         var _this = this;
42539      
42540         Roo.each(stylesheets, function(s) {
42541             if(!s.length){
42542                 return;
42543             }
42544             
42545             Roo.get(_this.iframe.contentDocument.head).createChild({
42546                 tag : 'link',
42547                 rel : 'stylesheet',
42548                 type : 'text/css',
42549                 href : s
42550             });
42551         });
42552
42553         
42554     },
42555     
42556     removeStylesheets : function()
42557     {
42558         var _this = this;
42559         
42560         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42561             s.remove();
42562         });
42563     }
42564     
42565     // hide stuff that is not compatible
42566     /**
42567      * @event blur
42568      * @hide
42569      */
42570     /**
42571      * @event change
42572      * @hide
42573      */
42574     /**
42575      * @event focus
42576      * @hide
42577      */
42578     /**
42579      * @event specialkey
42580      * @hide
42581      */
42582     /**
42583      * @cfg {String} fieldClass @hide
42584      */
42585     /**
42586      * @cfg {String} focusClass @hide
42587      */
42588     /**
42589      * @cfg {String} autoCreate @hide
42590      */
42591     /**
42592      * @cfg {String} inputType @hide
42593      */
42594     /**
42595      * @cfg {String} invalidClass @hide
42596      */
42597     /**
42598      * @cfg {String} invalidText @hide
42599      */
42600     /**
42601      * @cfg {String} msgFx @hide
42602      */
42603     /**
42604      * @cfg {String} validateOnBlur @hide
42605      */
42606 });
42607
42608 Roo.HtmlEditorCore.white = [
42609         'area', 'br', 'img', 'input', 'hr', 'wbr',
42610         
42611        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42612        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42613        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42614        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42615        'table',   'ul',         'xmp', 
42616        
42617        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42618       'thead',   'tr', 
42619      
42620       'dir', 'menu', 'ol', 'ul', 'dl',
42621        
42622       'embed',  'object'
42623 ];
42624
42625
42626 Roo.HtmlEditorCore.black = [
42627     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42628         'applet', // 
42629         'base',   'basefont', 'bgsound', 'blink',  'body', 
42630         'frame',  'frameset', 'head',    'html',   'ilayer', 
42631         'iframe', 'layer',  'link',     'meta',    'object',   
42632         'script', 'style' ,'title',  'xml' // clean later..
42633 ];
42634 Roo.HtmlEditorCore.clean = [
42635     'script', 'style', 'title', 'xml'
42636 ];
42637 Roo.HtmlEditorCore.remove = [
42638     'font'
42639 ];
42640 // attributes..
42641
42642 Roo.HtmlEditorCore.ablack = [
42643     'on'
42644 ];
42645     
42646 Roo.HtmlEditorCore.aclean = [ 
42647     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42648 ];
42649
42650 // protocols..
42651 Roo.HtmlEditorCore.pwhite= [
42652         'http',  'https',  'mailto'
42653 ];
42654
42655 // white listed style attributes.
42656 Roo.HtmlEditorCore.cwhite= [
42657       //  'text-align', /// default is to allow most things..
42658       
42659          
42660 //        'font-size'//??
42661 ];
42662
42663 // black listed style attributes.
42664 Roo.HtmlEditorCore.cblack= [
42665       //  'font-size' -- this can be set by the project 
42666 ];
42667
42668
42669 Roo.HtmlEditorCore.swapCodes   =[ 
42670     [    8211, "--" ], 
42671     [    8212, "--" ], 
42672     [    8216,  "'" ],  
42673     [    8217, "'" ],  
42674     [    8220, '"' ],  
42675     [    8221, '"' ],  
42676     [    8226, "*" ],  
42677     [    8230, "..." ]
42678 ]; 
42679
42680     //<script type="text/javascript">
42681
42682 /*
42683  * Ext JS Library 1.1.1
42684  * Copyright(c) 2006-2007, Ext JS, LLC.
42685  * Licence LGPL
42686  * 
42687  */
42688  
42689  
42690 Roo.form.HtmlEditor = function(config){
42691     
42692     
42693     
42694     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42695     
42696     if (!this.toolbars) {
42697         this.toolbars = [];
42698     }
42699     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42700     
42701     
42702 };
42703
42704 /**
42705  * @class Roo.form.HtmlEditor
42706  * @extends Roo.form.Field
42707  * Provides a lightweight HTML Editor component.
42708  *
42709  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42710  * 
42711  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42712  * supported by this editor.</b><br/><br/>
42713  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42714  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42715  */
42716 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42717     /**
42718      * @cfg {Boolean} clearUp
42719      */
42720     clearUp : true,
42721       /**
42722      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42723      */
42724     toolbars : false,
42725    
42726      /**
42727      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42728      *                        Roo.resizable.
42729      */
42730     resizable : false,
42731      /**
42732      * @cfg {Number} height (in pixels)
42733      */   
42734     height: 300,
42735    /**
42736      * @cfg {Number} width (in pixels)
42737      */   
42738     width: 500,
42739     
42740     /**
42741      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42742      * 
42743      */
42744     stylesheets: false,
42745     
42746     
42747      /**
42748      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42749      * 
42750      */
42751     cblack: false,
42752     /**
42753      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42754      * 
42755      */
42756     cwhite: false,
42757     
42758      /**
42759      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42760      * 
42761      */
42762     black: false,
42763     /**
42764      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42765      * 
42766      */
42767     white: false,
42768     
42769     // id of frame..
42770     frameId: false,
42771     
42772     // private properties
42773     validationEvent : false,
42774     deferHeight: true,
42775     initialized : false,
42776     activated : false,
42777     
42778     onFocus : Roo.emptyFn,
42779     iframePad:3,
42780     hideMode:'offsets',
42781     
42782     actionMode : 'container', // defaults to hiding it...
42783     
42784     defaultAutoCreate : { // modified by initCompnoent..
42785         tag: "textarea",
42786         style:"width:500px;height:300px;",
42787         autocomplete: "off"
42788     },
42789
42790     // private
42791     initComponent : function(){
42792         this.addEvents({
42793             /**
42794              * @event initialize
42795              * Fires when the editor is fully initialized (including the iframe)
42796              * @param {HtmlEditor} this
42797              */
42798             initialize: true,
42799             /**
42800              * @event activate
42801              * Fires when the editor is first receives the focus. Any insertion must wait
42802              * until after this event.
42803              * @param {HtmlEditor} this
42804              */
42805             activate: true,
42806              /**
42807              * @event beforesync
42808              * Fires before the textarea is updated with content from the editor iframe. Return false
42809              * to cancel the sync.
42810              * @param {HtmlEditor} this
42811              * @param {String} html
42812              */
42813             beforesync: true,
42814              /**
42815              * @event beforepush
42816              * Fires before the iframe editor is updated with content from the textarea. Return false
42817              * to cancel the push.
42818              * @param {HtmlEditor} this
42819              * @param {String} html
42820              */
42821             beforepush: true,
42822              /**
42823              * @event sync
42824              * Fires when the textarea is updated with content from the editor iframe.
42825              * @param {HtmlEditor} this
42826              * @param {String} html
42827              */
42828             sync: true,
42829              /**
42830              * @event push
42831              * Fires when the iframe editor is updated with content from the textarea.
42832              * @param {HtmlEditor} this
42833              * @param {String} html
42834              */
42835             push: true,
42836              /**
42837              * @event editmodechange
42838              * Fires when the editor switches edit modes
42839              * @param {HtmlEditor} this
42840              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42841              */
42842             editmodechange: true,
42843             /**
42844              * @event editorevent
42845              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42846              * @param {HtmlEditor} this
42847              */
42848             editorevent: true,
42849             /**
42850              * @event firstfocus
42851              * Fires when on first focus - needed by toolbars..
42852              * @param {HtmlEditor} this
42853              */
42854             firstfocus: true,
42855             /**
42856              * @event autosave
42857              * Auto save the htmlEditor value as a file into Events
42858              * @param {HtmlEditor} this
42859              */
42860             autosave: true,
42861             /**
42862              * @event savedpreview
42863              * preview the saved version of htmlEditor
42864              * @param {HtmlEditor} this
42865              */
42866             savedpreview: true,
42867             
42868             /**
42869             * @event stylesheetsclick
42870             * Fires when press the Sytlesheets button
42871             * @param {Roo.HtmlEditorCore} this
42872             */
42873             stylesheetsclick: true
42874         });
42875         this.defaultAutoCreate =  {
42876             tag: "textarea",
42877             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42878             autocomplete: "off"
42879         };
42880     },
42881
42882     /**
42883      * Protected method that will not generally be called directly. It
42884      * is called when the editor creates its toolbar. Override this method if you need to
42885      * add custom toolbar buttons.
42886      * @param {HtmlEditor} editor
42887      */
42888     createToolbar : function(editor){
42889         Roo.log("create toolbars");
42890         if (!editor.toolbars || !editor.toolbars.length) {
42891             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42892         }
42893         
42894         for (var i =0 ; i < editor.toolbars.length;i++) {
42895             editor.toolbars[i] = Roo.factory(
42896                     typeof(editor.toolbars[i]) == 'string' ?
42897                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42898                 Roo.form.HtmlEditor);
42899             editor.toolbars[i].init(editor);
42900         }
42901          
42902         
42903     },
42904
42905      
42906     // private
42907     onRender : function(ct, position)
42908     {
42909         var _t = this;
42910         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42911         
42912         this.wrap = this.el.wrap({
42913             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42914         });
42915         
42916         this.editorcore.onRender(ct, position);
42917          
42918         if (this.resizable) {
42919             this.resizeEl = new Roo.Resizable(this.wrap, {
42920                 pinned : true,
42921                 wrap: true,
42922                 dynamic : true,
42923                 minHeight : this.height,
42924                 height: this.height,
42925                 handles : this.resizable,
42926                 width: this.width,
42927                 listeners : {
42928                     resize : function(r, w, h) {
42929                         _t.onResize(w,h); // -something
42930                     }
42931                 }
42932             });
42933             
42934         }
42935         this.createToolbar(this);
42936        
42937         
42938         if(!this.width){
42939             this.setSize(this.wrap.getSize());
42940         }
42941         if (this.resizeEl) {
42942             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42943             // should trigger onReize..
42944         }
42945         
42946         this.keyNav = new Roo.KeyNav(this.el, {
42947             
42948             "tab" : function(e){
42949                 e.preventDefault();
42950                 
42951                 var value = this.getValue();
42952                 
42953                 var start = this.el.dom.selectionStart;
42954                 var end = this.el.dom.selectionEnd;
42955                 
42956                 if(!e.shiftKey){
42957                     
42958                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
42959                     this.el.dom.setSelectionRange(end + 1, end + 1);
42960                     return;
42961                 }
42962                 
42963                 var f = value.substring(0, start).split("\t");
42964                 
42965                 if(f.pop().length != 0){
42966                     return;
42967                 }
42968                 
42969                 this.setValue(f.join("\t") + value.substring(end));
42970                 this.el.dom.setSelectionRange(start - 1, start - 1);
42971                 
42972             },
42973             
42974             "home" : function(e){
42975                 e.preventDefault();
42976                 
42977                 var curr = this.el.dom.selectionStart;
42978                 var lines = this.getValue().split("\n");
42979                 
42980                 if(!lines.length){
42981                     return;
42982                 }
42983                 
42984                 if(e.ctrlKey){
42985                     this.el.dom.setSelectionRange(0, 0);
42986                     return;
42987                 }
42988                 
42989                 var pos = 0;
42990                 
42991                 for (var i = 0; i < lines.length;i++) {
42992                     pos += lines[i].length;
42993                     
42994                     if(i != 0){
42995                         pos += 1;
42996                     }
42997                     
42998                     if(pos < curr){
42999                         continue;
43000                     }
43001                     
43002                     pos -= lines[i].length;
43003                     
43004                     break;
43005                 }
43006                 
43007                 if(!e.shiftKey){
43008                     this.el.dom.setSelectionRange(pos, pos);
43009                     return;
43010                 }
43011                 
43012                 this.el.dom.selectionStart = pos;
43013                 this.el.dom.selectionEnd = curr;
43014             },
43015             
43016             "end" : function(e){
43017                 e.preventDefault();
43018                 
43019                 var curr = this.el.dom.selectionStart;
43020                 var lines = this.getValue().split("\n");
43021                 
43022                 if(!lines.length){
43023                     return;
43024                 }
43025                 
43026                 if(e.ctrlKey){
43027                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43028                     return;
43029                 }
43030                 
43031                 var pos = 0;
43032                 
43033                 for (var i = 0; i < lines.length;i++) {
43034                     
43035                     pos += lines[i].length;
43036                     
43037                     if(i != 0){
43038                         pos += 1;
43039                     }
43040                     
43041                     if(pos < curr){
43042                         continue;
43043                     }
43044                     
43045                     break;
43046                 }
43047                 
43048                 if(!e.shiftKey){
43049                     this.el.dom.setSelectionRange(pos, pos);
43050                     return;
43051                 }
43052                 
43053                 this.el.dom.selectionStart = curr;
43054                 this.el.dom.selectionEnd = pos;
43055             },
43056
43057             scope : this,
43058
43059             doRelay : function(foo, bar, hname){
43060                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43061             },
43062
43063             forceKeyDown: true
43064         });
43065         
43066 //        if(this.autosave && this.w){
43067 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43068 //        }
43069     },
43070
43071     // private
43072     onResize : function(w, h)
43073     {
43074         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43075         var ew = false;
43076         var eh = false;
43077         
43078         if(this.el ){
43079             if(typeof w == 'number'){
43080                 var aw = w - this.wrap.getFrameWidth('lr');
43081                 this.el.setWidth(this.adjustWidth('textarea', aw));
43082                 ew = aw;
43083             }
43084             if(typeof h == 'number'){
43085                 var tbh = 0;
43086                 for (var i =0; i < this.toolbars.length;i++) {
43087                     // fixme - ask toolbars for heights?
43088                     tbh += this.toolbars[i].tb.el.getHeight();
43089                     if (this.toolbars[i].footer) {
43090                         tbh += this.toolbars[i].footer.el.getHeight();
43091                     }
43092                 }
43093                 
43094                 
43095                 
43096                 
43097                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43098                 ah -= 5; // knock a few pixes off for look..
43099 //                Roo.log(ah);
43100                 this.el.setHeight(this.adjustWidth('textarea', ah));
43101                 var eh = ah;
43102             }
43103         }
43104         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43105         this.editorcore.onResize(ew,eh);
43106         
43107     },
43108
43109     /**
43110      * Toggles the editor between standard and source edit mode.
43111      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43112      */
43113     toggleSourceEdit : function(sourceEditMode)
43114     {
43115         this.editorcore.toggleSourceEdit(sourceEditMode);
43116         
43117         if(this.editorcore.sourceEditMode){
43118             Roo.log('editor - showing textarea');
43119             
43120 //            Roo.log('in');
43121 //            Roo.log(this.syncValue());
43122             this.editorcore.syncValue();
43123             this.el.removeClass('x-hidden');
43124             this.el.dom.removeAttribute('tabIndex');
43125             this.el.focus();
43126             
43127             for (var i = 0; i < this.toolbars.length; i++) {
43128                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43129                     this.toolbars[i].tb.hide();
43130                     this.toolbars[i].footer.hide();
43131                 }
43132             }
43133             
43134         }else{
43135             Roo.log('editor - hiding textarea');
43136 //            Roo.log('out')
43137 //            Roo.log(this.pushValue()); 
43138             this.editorcore.pushValue();
43139             
43140             this.el.addClass('x-hidden');
43141             this.el.dom.setAttribute('tabIndex', -1);
43142             
43143             for (var i = 0; i < this.toolbars.length; i++) {
43144                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43145                     this.toolbars[i].tb.show();
43146                     this.toolbars[i].footer.show();
43147                 }
43148             }
43149             
43150             //this.deferFocus();
43151         }
43152         
43153         this.setSize(this.wrap.getSize());
43154         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43155         
43156         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43157     },
43158  
43159     // private (for BoxComponent)
43160     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43161
43162     // private (for BoxComponent)
43163     getResizeEl : function(){
43164         return this.wrap;
43165     },
43166
43167     // private (for BoxComponent)
43168     getPositionEl : function(){
43169         return this.wrap;
43170     },
43171
43172     // private
43173     initEvents : function(){
43174         this.originalValue = this.getValue();
43175     },
43176
43177     /**
43178      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43179      * @method
43180      */
43181     markInvalid : Roo.emptyFn,
43182     /**
43183      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43184      * @method
43185      */
43186     clearInvalid : Roo.emptyFn,
43187
43188     setValue : function(v){
43189         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43190         this.editorcore.pushValue();
43191     },
43192
43193      
43194     // private
43195     deferFocus : function(){
43196         this.focus.defer(10, this);
43197     },
43198
43199     // doc'ed in Field
43200     focus : function(){
43201         this.editorcore.focus();
43202         
43203     },
43204       
43205
43206     // private
43207     onDestroy : function(){
43208         
43209         
43210         
43211         if(this.rendered){
43212             
43213             for (var i =0; i < this.toolbars.length;i++) {
43214                 // fixme - ask toolbars for heights?
43215                 this.toolbars[i].onDestroy();
43216             }
43217             
43218             this.wrap.dom.innerHTML = '';
43219             this.wrap.remove();
43220         }
43221     },
43222
43223     // private
43224     onFirstFocus : function(){
43225         //Roo.log("onFirstFocus");
43226         this.editorcore.onFirstFocus();
43227          for (var i =0; i < this.toolbars.length;i++) {
43228             this.toolbars[i].onFirstFocus();
43229         }
43230         
43231     },
43232     
43233     // private
43234     syncValue : function()
43235     {
43236         this.editorcore.syncValue();
43237     },
43238     
43239     pushValue : function()
43240     {
43241         this.editorcore.pushValue();
43242     },
43243     
43244     setStylesheets : function(stylesheets)
43245     {
43246         this.editorcore.setStylesheets(stylesheets);
43247     },
43248     
43249     removeStylesheets : function()
43250     {
43251         this.editorcore.removeStylesheets();
43252     }
43253      
43254     
43255     // hide stuff that is not compatible
43256     /**
43257      * @event blur
43258      * @hide
43259      */
43260     /**
43261      * @event change
43262      * @hide
43263      */
43264     /**
43265      * @event focus
43266      * @hide
43267      */
43268     /**
43269      * @event specialkey
43270      * @hide
43271      */
43272     /**
43273      * @cfg {String} fieldClass @hide
43274      */
43275     /**
43276      * @cfg {String} focusClass @hide
43277      */
43278     /**
43279      * @cfg {String} autoCreate @hide
43280      */
43281     /**
43282      * @cfg {String} inputType @hide
43283      */
43284     /**
43285      * @cfg {String} invalidClass @hide
43286      */
43287     /**
43288      * @cfg {String} invalidText @hide
43289      */
43290     /**
43291      * @cfg {String} msgFx @hide
43292      */
43293     /**
43294      * @cfg {String} validateOnBlur @hide
43295      */
43296 });
43297  
43298     // <script type="text/javascript">
43299 /*
43300  * Based on
43301  * Ext JS Library 1.1.1
43302  * Copyright(c) 2006-2007, Ext JS, LLC.
43303  *  
43304  
43305  */
43306
43307 /**
43308  * @class Roo.form.HtmlEditorToolbar1
43309  * Basic Toolbar
43310  * 
43311  * Usage:
43312  *
43313  new Roo.form.HtmlEditor({
43314     ....
43315     toolbars : [
43316         new Roo.form.HtmlEditorToolbar1({
43317             disable : { fonts: 1 , format: 1, ..., ... , ...],
43318             btns : [ .... ]
43319         })
43320     }
43321      
43322  * 
43323  * @cfg {Object} disable List of elements to disable..
43324  * @cfg {Array} btns List of additional buttons.
43325  * 
43326  * 
43327  * NEEDS Extra CSS? 
43328  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43329  */
43330  
43331 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43332 {
43333     
43334     Roo.apply(this, config);
43335     
43336     // default disabled, based on 'good practice'..
43337     this.disable = this.disable || {};
43338     Roo.applyIf(this.disable, {
43339         fontSize : true,
43340         colors : true,
43341         specialElements : true
43342     });
43343     
43344     
43345     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43346     // dont call parent... till later.
43347 }
43348
43349 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43350     
43351     tb: false,
43352     
43353     rendered: false,
43354     
43355     editor : false,
43356     editorcore : false,
43357     /**
43358      * @cfg {Object} disable  List of toolbar elements to disable
43359          
43360      */
43361     disable : false,
43362     
43363     
43364      /**
43365      * @cfg {String} createLinkText The default text for the create link prompt
43366      */
43367     createLinkText : 'Please enter the URL for the link:',
43368     /**
43369      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43370      */
43371     defaultLinkValue : 'http:/'+'/',
43372    
43373     
43374       /**
43375      * @cfg {Array} fontFamilies An array of available font families
43376      */
43377     fontFamilies : [
43378         'Arial',
43379         'Courier New',
43380         'Tahoma',
43381         'Times New Roman',
43382         'Verdana'
43383     ],
43384     
43385     specialChars : [
43386            "&#169;",
43387           "&#174;",     
43388           "&#8482;",    
43389           "&#163;" ,    
43390          // "&#8212;",    
43391           "&#8230;",    
43392           "&#247;" ,    
43393         //  "&#225;" ,     ?? a acute?
43394            "&#8364;"    , //Euro
43395        //   "&#8220;"    ,
43396         //  "&#8221;"    ,
43397         //  "&#8226;"    ,
43398           "&#176;"  //   , // degrees
43399
43400          // "&#233;"     , // e ecute
43401          // "&#250;"     , // u ecute?
43402     ],
43403     
43404     specialElements : [
43405         {
43406             text: "Insert Table",
43407             xtype: 'MenuItem',
43408             xns : Roo.Menu,
43409             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43410                 
43411         },
43412         {    
43413             text: "Insert Image",
43414             xtype: 'MenuItem',
43415             xns : Roo.Menu,
43416             ihtml : '<img src="about:blank"/>'
43417             
43418         }
43419         
43420          
43421     ],
43422     
43423     
43424     inputElements : [ 
43425             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43426             "input:submit", "input:button", "select", "textarea", "label" ],
43427     formats : [
43428         ["p"] ,  
43429         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43430         ["pre"],[ "code"], 
43431         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43432         ['div'],['span']
43433     ],
43434     
43435     cleanStyles : [
43436         "font-size"
43437     ],
43438      /**
43439      * @cfg {String} defaultFont default font to use.
43440      */
43441     defaultFont: 'tahoma',
43442    
43443     fontSelect : false,
43444     
43445     
43446     formatCombo : false,
43447     
43448     init : function(editor)
43449     {
43450         this.editor = editor;
43451         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43452         var editorcore = this.editorcore;
43453         
43454         var _t = this;
43455         
43456         var fid = editorcore.frameId;
43457         var etb = this;
43458         function btn(id, toggle, handler){
43459             var xid = fid + '-'+ id ;
43460             return {
43461                 id : xid,
43462                 cmd : id,
43463                 cls : 'x-btn-icon x-edit-'+id,
43464                 enableToggle:toggle !== false,
43465                 scope: _t, // was editor...
43466                 handler:handler||_t.relayBtnCmd,
43467                 clickEvent:'mousedown',
43468                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43469                 tabIndex:-1
43470             };
43471         }
43472         
43473         
43474         
43475         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43476         this.tb = tb;
43477          // stop form submits
43478         tb.el.on('click', function(e){
43479             e.preventDefault(); // what does this do?
43480         });
43481
43482         if(!this.disable.font) { // && !Roo.isSafari){
43483             /* why no safari for fonts 
43484             editor.fontSelect = tb.el.createChild({
43485                 tag:'select',
43486                 tabIndex: -1,
43487                 cls:'x-font-select',
43488                 html: this.createFontOptions()
43489             });
43490             
43491             editor.fontSelect.on('change', function(){
43492                 var font = editor.fontSelect.dom.value;
43493                 editor.relayCmd('fontname', font);
43494                 editor.deferFocus();
43495             }, editor);
43496             
43497             tb.add(
43498                 editor.fontSelect.dom,
43499                 '-'
43500             );
43501             */
43502             
43503         };
43504         if(!this.disable.formats){
43505             this.formatCombo = new Roo.form.ComboBox({
43506                 store: new Roo.data.SimpleStore({
43507                     id : 'tag',
43508                     fields: ['tag'],
43509                     data : this.formats // from states.js
43510                 }),
43511                 blockFocus : true,
43512                 name : '',
43513                 //autoCreate : {tag: "div",  size: "20"},
43514                 displayField:'tag',
43515                 typeAhead: false,
43516                 mode: 'local',
43517                 editable : false,
43518                 triggerAction: 'all',
43519                 emptyText:'Add tag',
43520                 selectOnFocus:true,
43521                 width:135,
43522                 listeners : {
43523                     'select': function(c, r, i) {
43524                         editorcore.insertTag(r.get('tag'));
43525                         editor.focus();
43526                     }
43527                 }
43528
43529             });
43530             tb.addField(this.formatCombo);
43531             
43532         }
43533         
43534         if(!this.disable.format){
43535             tb.add(
43536                 btn('bold'),
43537                 btn('italic'),
43538                 btn('underline')
43539             );
43540         };
43541         if(!this.disable.fontSize){
43542             tb.add(
43543                 '-',
43544                 
43545                 
43546                 btn('increasefontsize', false, editorcore.adjustFont),
43547                 btn('decreasefontsize', false, editorcore.adjustFont)
43548             );
43549         };
43550         
43551         
43552         if(!this.disable.colors){
43553             tb.add(
43554                 '-', {
43555                     id:editorcore.frameId +'-forecolor',
43556                     cls:'x-btn-icon x-edit-forecolor',
43557                     clickEvent:'mousedown',
43558                     tooltip: this.buttonTips['forecolor'] || undefined,
43559                     tabIndex:-1,
43560                     menu : new Roo.menu.ColorMenu({
43561                         allowReselect: true,
43562                         focus: Roo.emptyFn,
43563                         value:'000000',
43564                         plain:true,
43565                         selectHandler: function(cp, color){
43566                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43567                             editor.deferFocus();
43568                         },
43569                         scope: editorcore,
43570                         clickEvent:'mousedown'
43571                     })
43572                 }, {
43573                     id:editorcore.frameId +'backcolor',
43574                     cls:'x-btn-icon x-edit-backcolor',
43575                     clickEvent:'mousedown',
43576                     tooltip: this.buttonTips['backcolor'] || undefined,
43577                     tabIndex:-1,
43578                     menu : new Roo.menu.ColorMenu({
43579                         focus: Roo.emptyFn,
43580                         value:'FFFFFF',
43581                         plain:true,
43582                         allowReselect: true,
43583                         selectHandler: function(cp, color){
43584                             if(Roo.isGecko){
43585                                 editorcore.execCmd('useCSS', false);
43586                                 editorcore.execCmd('hilitecolor', color);
43587                                 editorcore.execCmd('useCSS', true);
43588                                 editor.deferFocus();
43589                             }else{
43590                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43591                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43592                                 editor.deferFocus();
43593                             }
43594                         },
43595                         scope:editorcore,
43596                         clickEvent:'mousedown'
43597                     })
43598                 }
43599             );
43600         };
43601         // now add all the items...
43602         
43603
43604         if(!this.disable.alignments){
43605             tb.add(
43606                 '-',
43607                 btn('justifyleft'),
43608                 btn('justifycenter'),
43609                 btn('justifyright')
43610             );
43611         };
43612
43613         //if(!Roo.isSafari){
43614             if(!this.disable.links){
43615                 tb.add(
43616                     '-',
43617                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43618                 );
43619             };
43620
43621             if(!this.disable.lists){
43622                 tb.add(
43623                     '-',
43624                     btn('insertorderedlist'),
43625                     btn('insertunorderedlist')
43626                 );
43627             }
43628             if(!this.disable.sourceEdit){
43629                 tb.add(
43630                     '-',
43631                     btn('sourceedit', true, function(btn){
43632                         this.toggleSourceEdit(btn.pressed);
43633                     })
43634                 );
43635             }
43636         //}
43637         
43638         var smenu = { };
43639         // special menu.. - needs to be tidied up..
43640         if (!this.disable.special) {
43641             smenu = {
43642                 text: "&#169;",
43643                 cls: 'x-edit-none',
43644                 
43645                 menu : {
43646                     items : []
43647                 }
43648             };
43649             for (var i =0; i < this.specialChars.length; i++) {
43650                 smenu.menu.items.push({
43651                     
43652                     html: this.specialChars[i],
43653                     handler: function(a,b) {
43654                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43655                         //editor.insertAtCursor(a.html);
43656                         
43657                     },
43658                     tabIndex:-1
43659                 });
43660             }
43661             
43662             
43663             tb.add(smenu);
43664             
43665             
43666         }
43667         
43668         var cmenu = { };
43669         if (!this.disable.cleanStyles) {
43670             cmenu = {
43671                 cls: 'x-btn-icon x-btn-clear',
43672                 
43673                 menu : {
43674                     items : []
43675                 }
43676             };
43677             for (var i =0; i < this.cleanStyles.length; i++) {
43678                 cmenu.menu.items.push({
43679                     actiontype : this.cleanStyles[i],
43680                     html: 'Remove ' + this.cleanStyles[i],
43681                     handler: function(a,b) {
43682 //                        Roo.log(a);
43683 //                        Roo.log(b);
43684                         var c = Roo.get(editorcore.doc.body);
43685                         c.select('[style]').each(function(s) {
43686                             s.dom.style.removeProperty(a.actiontype);
43687                         });
43688                         editorcore.syncValue();
43689                     },
43690                     tabIndex:-1
43691                 });
43692             }
43693             cmenu.menu.items.push({
43694                 actiontype : 'word',
43695                 html: 'Remove MS Word Formating',
43696                 handler: function(a,b) {
43697                     editorcore.cleanWord();
43698                     editorcore.syncValue();
43699                 },
43700                 tabIndex:-1
43701             });
43702             
43703             cmenu.menu.items.push({
43704                 actiontype : 'all',
43705                 html: 'Remove All Styles',
43706                 handler: function(a,b) {
43707                     
43708                     var c = Roo.get(editorcore.doc.body);
43709                     c.select('[style]').each(function(s) {
43710                         s.dom.removeAttribute('style');
43711                     });
43712                     editorcore.syncValue();
43713                 },
43714                 tabIndex:-1
43715             });
43716              cmenu.menu.items.push({
43717                 actiontype : 'word',
43718                 html: 'Tidy HTML Source',
43719                 handler: function(a,b) {
43720                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43721                     editorcore.syncValue();
43722                 },
43723                 tabIndex:-1
43724             });
43725             
43726             
43727             tb.add(cmenu);
43728         }
43729          
43730         if (!this.disable.specialElements) {
43731             var semenu = {
43732                 text: "Other;",
43733                 cls: 'x-edit-none',
43734                 menu : {
43735                     items : []
43736                 }
43737             };
43738             for (var i =0; i < this.specialElements.length; i++) {
43739                 semenu.menu.items.push(
43740                     Roo.apply({ 
43741                         handler: function(a,b) {
43742                             editor.insertAtCursor(this.ihtml);
43743                         }
43744                     }, this.specialElements[i])
43745                 );
43746                     
43747             }
43748             
43749             tb.add(semenu);
43750             
43751             
43752         }
43753          
43754         
43755         if (this.btns) {
43756             for(var i =0; i< this.btns.length;i++) {
43757                 var b = Roo.factory(this.btns[i],Roo.form);
43758                 b.cls =  'x-edit-none';
43759                 
43760                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43761                     b.cls += ' x-init-enable';
43762                 }
43763                 
43764                 b.scope = editorcore;
43765                 tb.add(b);
43766             }
43767         
43768         }
43769         
43770         
43771         
43772         // disable everything...
43773         
43774         this.tb.items.each(function(item){
43775             
43776            if(
43777                 item.id != editorcore.frameId+ '-sourceedit' && 
43778                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43779             ){
43780                 
43781                 item.disable();
43782             }
43783         });
43784         this.rendered = true;
43785         
43786         // the all the btns;
43787         editor.on('editorevent', this.updateToolbar, this);
43788         // other toolbars need to implement this..
43789         //editor.on('editmodechange', this.updateToolbar, this);
43790     },
43791     
43792     
43793     relayBtnCmd : function(btn) {
43794         this.editorcore.relayCmd(btn.cmd);
43795     },
43796     // private used internally
43797     createLink : function(){
43798         Roo.log("create link?");
43799         var url = prompt(this.createLinkText, this.defaultLinkValue);
43800         if(url && url != 'http:/'+'/'){
43801             this.editorcore.relayCmd('createlink', url);
43802         }
43803     },
43804
43805     
43806     /**
43807      * Protected method that will not generally be called directly. It triggers
43808      * a toolbar update by reading the markup state of the current selection in the editor.
43809      */
43810     updateToolbar: function(){
43811
43812         if(!this.editorcore.activated){
43813             this.editor.onFirstFocus();
43814             return;
43815         }
43816
43817         var btns = this.tb.items.map, 
43818             doc = this.editorcore.doc,
43819             frameId = this.editorcore.frameId;
43820
43821         if(!this.disable.font && !Roo.isSafari){
43822             /*
43823             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43824             if(name != this.fontSelect.dom.value){
43825                 this.fontSelect.dom.value = name;
43826             }
43827             */
43828         }
43829         if(!this.disable.format){
43830             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43831             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43832             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43833         }
43834         if(!this.disable.alignments){
43835             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43836             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43837             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43838         }
43839         if(!Roo.isSafari && !this.disable.lists){
43840             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43841             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43842         }
43843         
43844         var ans = this.editorcore.getAllAncestors();
43845         if (this.formatCombo) {
43846             
43847             
43848             var store = this.formatCombo.store;
43849             this.formatCombo.setValue("");
43850             for (var i =0; i < ans.length;i++) {
43851                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43852                     // select it..
43853                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43854                     break;
43855                 }
43856             }
43857         }
43858         
43859         
43860         
43861         // hides menus... - so this cant be on a menu...
43862         Roo.menu.MenuMgr.hideAll();
43863
43864         //this.editorsyncValue();
43865     },
43866    
43867     
43868     createFontOptions : function(){
43869         var buf = [], fs = this.fontFamilies, ff, lc;
43870         
43871         
43872         
43873         for(var i = 0, len = fs.length; i< len; i++){
43874             ff = fs[i];
43875             lc = ff.toLowerCase();
43876             buf.push(
43877                 '<option value="',lc,'" style="font-family:',ff,';"',
43878                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43879                     ff,
43880                 '</option>'
43881             );
43882         }
43883         return buf.join('');
43884     },
43885     
43886     toggleSourceEdit : function(sourceEditMode){
43887         
43888         Roo.log("toolbar toogle");
43889         if(sourceEditMode === undefined){
43890             sourceEditMode = !this.sourceEditMode;
43891         }
43892         this.sourceEditMode = sourceEditMode === true;
43893         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43894         // just toggle the button?
43895         if(btn.pressed !== this.sourceEditMode){
43896             btn.toggle(this.sourceEditMode);
43897             return;
43898         }
43899         
43900         if(sourceEditMode){
43901             Roo.log("disabling buttons");
43902             this.tb.items.each(function(item){
43903                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
43904                     item.disable();
43905                 }
43906             });
43907           
43908         }else{
43909             Roo.log("enabling buttons");
43910             if(this.editorcore.initialized){
43911                 this.tb.items.each(function(item){
43912                     item.enable();
43913                 });
43914             }
43915             
43916         }
43917         Roo.log("calling toggole on editor");
43918         // tell the editor that it's been pressed..
43919         this.editor.toggleSourceEdit(sourceEditMode);
43920        
43921     },
43922      /**
43923      * Object collection of toolbar tooltips for the buttons in the editor. The key
43924      * is the command id associated with that button and the value is a valid QuickTips object.
43925      * For example:
43926 <pre><code>
43927 {
43928     bold : {
43929         title: 'Bold (Ctrl+B)',
43930         text: 'Make the selected text bold.',
43931         cls: 'x-html-editor-tip'
43932     },
43933     italic : {
43934         title: 'Italic (Ctrl+I)',
43935         text: 'Make the selected text italic.',
43936         cls: 'x-html-editor-tip'
43937     },
43938     ...
43939 </code></pre>
43940     * @type Object
43941      */
43942     buttonTips : {
43943         bold : {
43944             title: 'Bold (Ctrl+B)',
43945             text: 'Make the selected text bold.',
43946             cls: 'x-html-editor-tip'
43947         },
43948         italic : {
43949             title: 'Italic (Ctrl+I)',
43950             text: 'Make the selected text italic.',
43951             cls: 'x-html-editor-tip'
43952         },
43953         underline : {
43954             title: 'Underline (Ctrl+U)',
43955             text: 'Underline the selected text.',
43956             cls: 'x-html-editor-tip'
43957         },
43958         increasefontsize : {
43959             title: 'Grow Text',
43960             text: 'Increase the font size.',
43961             cls: 'x-html-editor-tip'
43962         },
43963         decreasefontsize : {
43964             title: 'Shrink Text',
43965             text: 'Decrease the font size.',
43966             cls: 'x-html-editor-tip'
43967         },
43968         backcolor : {
43969             title: 'Text Highlight Color',
43970             text: 'Change the background color of the selected text.',
43971             cls: 'x-html-editor-tip'
43972         },
43973         forecolor : {
43974             title: 'Font Color',
43975             text: 'Change the color of the selected text.',
43976             cls: 'x-html-editor-tip'
43977         },
43978         justifyleft : {
43979             title: 'Align Text Left',
43980             text: 'Align text to the left.',
43981             cls: 'x-html-editor-tip'
43982         },
43983         justifycenter : {
43984             title: 'Center Text',
43985             text: 'Center text in the editor.',
43986             cls: 'x-html-editor-tip'
43987         },
43988         justifyright : {
43989             title: 'Align Text Right',
43990             text: 'Align text to the right.',
43991             cls: 'x-html-editor-tip'
43992         },
43993         insertunorderedlist : {
43994             title: 'Bullet List',
43995             text: 'Start a bulleted list.',
43996             cls: 'x-html-editor-tip'
43997         },
43998         insertorderedlist : {
43999             title: 'Numbered List',
44000             text: 'Start a numbered list.',
44001             cls: 'x-html-editor-tip'
44002         },
44003         createlink : {
44004             title: 'Hyperlink',
44005             text: 'Make the selected text a hyperlink.',
44006             cls: 'x-html-editor-tip'
44007         },
44008         sourceedit : {
44009             title: 'Source Edit',
44010             text: 'Switch to source editing mode.',
44011             cls: 'x-html-editor-tip'
44012         }
44013     },
44014     // private
44015     onDestroy : function(){
44016         if(this.rendered){
44017             
44018             this.tb.items.each(function(item){
44019                 if(item.menu){
44020                     item.menu.removeAll();
44021                     if(item.menu.el){
44022                         item.menu.el.destroy();
44023                     }
44024                 }
44025                 item.destroy();
44026             });
44027              
44028         }
44029     },
44030     onFirstFocus: function() {
44031         this.tb.items.each(function(item){
44032            item.enable();
44033         });
44034     }
44035 });
44036
44037
44038
44039
44040 // <script type="text/javascript">
44041 /*
44042  * Based on
44043  * Ext JS Library 1.1.1
44044  * Copyright(c) 2006-2007, Ext JS, LLC.
44045  *  
44046  
44047  */
44048
44049  
44050 /**
44051  * @class Roo.form.HtmlEditor.ToolbarContext
44052  * Context Toolbar
44053  * 
44054  * Usage:
44055  *
44056  new Roo.form.HtmlEditor({
44057     ....
44058     toolbars : [
44059         { xtype: 'ToolbarStandard', styles : {} }
44060         { xtype: 'ToolbarContext', disable : {} }
44061     ]
44062 })
44063
44064      
44065  * 
44066  * @config : {Object} disable List of elements to disable.. (not done yet.)
44067  * @config : {Object} styles  Map of styles available.
44068  * 
44069  */
44070
44071 Roo.form.HtmlEditor.ToolbarContext = function(config)
44072 {
44073     
44074     Roo.apply(this, config);
44075     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44076     // dont call parent... till later.
44077     this.styles = this.styles || {};
44078 }
44079
44080  
44081
44082 Roo.form.HtmlEditor.ToolbarContext.types = {
44083     'IMG' : {
44084         width : {
44085             title: "Width",
44086             width: 40
44087         },
44088         height:  {
44089             title: "Height",
44090             width: 40
44091         },
44092         align: {
44093             title: "Align",
44094             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44095             width : 80
44096             
44097         },
44098         border: {
44099             title: "Border",
44100             width: 40
44101         },
44102         alt: {
44103             title: "Alt",
44104             width: 120
44105         },
44106         src : {
44107             title: "Src",
44108             width: 220
44109         }
44110         
44111     },
44112     'A' : {
44113         name : {
44114             title: "Name",
44115             width: 50
44116         },
44117         target:  {
44118             title: "Target",
44119             width: 120
44120         },
44121         href:  {
44122             title: "Href",
44123             width: 220
44124         } // border?
44125         
44126     },
44127     'TABLE' : {
44128         rows : {
44129             title: "Rows",
44130             width: 20
44131         },
44132         cols : {
44133             title: "Cols",
44134             width: 20
44135         },
44136         width : {
44137             title: "Width",
44138             width: 40
44139         },
44140         height : {
44141             title: "Height",
44142             width: 40
44143         },
44144         border : {
44145             title: "Border",
44146             width: 20
44147         }
44148     },
44149     'TD' : {
44150         width : {
44151             title: "Width",
44152             width: 40
44153         },
44154         height : {
44155             title: "Height",
44156             width: 40
44157         },   
44158         align: {
44159             title: "Align",
44160             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44161             width: 80
44162         },
44163         valign: {
44164             title: "Valign",
44165             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44166             width: 80
44167         },
44168         colspan: {
44169             title: "Colspan",
44170             width: 20
44171             
44172         },
44173          'font-family'  : {
44174             title : "Font",
44175             style : 'fontFamily',
44176             displayField: 'display',
44177             optname : 'font-family',
44178             width: 140
44179         }
44180     },
44181     'INPUT' : {
44182         name : {
44183             title: "name",
44184             width: 120
44185         },
44186         value : {
44187             title: "Value",
44188             width: 120
44189         },
44190         width : {
44191             title: "Width",
44192             width: 40
44193         }
44194     },
44195     'LABEL' : {
44196         'for' : {
44197             title: "For",
44198             width: 120
44199         }
44200     },
44201     'TEXTAREA' : {
44202           name : {
44203             title: "name",
44204             width: 120
44205         },
44206         rows : {
44207             title: "Rows",
44208             width: 20
44209         },
44210         cols : {
44211             title: "Cols",
44212             width: 20
44213         }
44214     },
44215     'SELECT' : {
44216         name : {
44217             title: "name",
44218             width: 120
44219         },
44220         selectoptions : {
44221             title: "Options",
44222             width: 200
44223         }
44224     },
44225     
44226     // should we really allow this??
44227     // should this just be 
44228     'BODY' : {
44229         title : {
44230             title: "Title",
44231             width: 200,
44232             disabled : true
44233         }
44234     },
44235     'SPAN' : {
44236         'font-family'  : {
44237             title : "Font",
44238             style : 'fontFamily',
44239             displayField: 'display',
44240             optname : 'font-family',
44241             width: 140
44242         }
44243     },
44244     'DIV' : {
44245         'font-family'  : {
44246             title : "Font",
44247             style : 'fontFamily',
44248             displayField: 'display',
44249             optname : 'font-family',
44250             width: 140
44251         }
44252     },
44253      'P' : {
44254         'font-family'  : {
44255             title : "Font",
44256             style : 'fontFamily',
44257             displayField: 'display',
44258             optname : 'font-family',
44259             width: 140
44260         }
44261     },
44262     
44263     '*' : {
44264         // empty..
44265     }
44266
44267 };
44268
44269 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44270 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44271
44272 Roo.form.HtmlEditor.ToolbarContext.options = {
44273         'font-family'  : [ 
44274                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44275                 [ 'Courier New', 'Courier New'],
44276                 [ 'Tahoma', 'Tahoma'],
44277                 [ 'Times New Roman,serif', 'Times'],
44278                 [ 'Verdana','Verdana' ]
44279         ]
44280 };
44281
44282 // fixme - these need to be configurable..
44283  
44284
44285 Roo.form.HtmlEditor.ToolbarContext.types
44286
44287
44288 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44289     
44290     tb: false,
44291     
44292     rendered: false,
44293     
44294     editor : false,
44295     editorcore : false,
44296     /**
44297      * @cfg {Object} disable  List of toolbar elements to disable
44298          
44299      */
44300     disable : false,
44301     /**
44302      * @cfg {Object} styles List of styles 
44303      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44304      *
44305      * These must be defined in the page, so they get rendered correctly..
44306      * .headline { }
44307      * TD.underline { }
44308      * 
44309      */
44310     styles : false,
44311     
44312     options: false,
44313     
44314     toolbars : false,
44315     
44316     init : function(editor)
44317     {
44318         this.editor = editor;
44319         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44320         var editorcore = this.editorcore;
44321         
44322         var fid = editorcore.frameId;
44323         var etb = this;
44324         function btn(id, toggle, handler){
44325             var xid = fid + '-'+ id ;
44326             return {
44327                 id : xid,
44328                 cmd : id,
44329                 cls : 'x-btn-icon x-edit-'+id,
44330                 enableToggle:toggle !== false,
44331                 scope: editorcore, // was editor...
44332                 handler:handler||editorcore.relayBtnCmd,
44333                 clickEvent:'mousedown',
44334                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44335                 tabIndex:-1
44336             };
44337         }
44338         // create a new element.
44339         var wdiv = editor.wrap.createChild({
44340                 tag: 'div'
44341             }, editor.wrap.dom.firstChild.nextSibling, true);
44342         
44343         // can we do this more than once??
44344         
44345          // stop form submits
44346       
44347  
44348         // disable everything...
44349         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44350         this.toolbars = {};
44351            
44352         for (var i in  ty) {
44353           
44354             this.toolbars[i] = this.buildToolbar(ty[i],i);
44355         }
44356         this.tb = this.toolbars.BODY;
44357         this.tb.el.show();
44358         this.buildFooter();
44359         this.footer.show();
44360         editor.on('hide', function( ) { this.footer.hide() }, this);
44361         editor.on('show', function( ) { this.footer.show() }, this);
44362         
44363          
44364         this.rendered = true;
44365         
44366         // the all the btns;
44367         editor.on('editorevent', this.updateToolbar, this);
44368         // other toolbars need to implement this..
44369         //editor.on('editmodechange', this.updateToolbar, this);
44370     },
44371     
44372     
44373     
44374     /**
44375      * Protected method that will not generally be called directly. It triggers
44376      * a toolbar update by reading the markup state of the current selection in the editor.
44377      */
44378     updateToolbar: function(editor,ev,sel){
44379
44380         //Roo.log(ev);
44381         // capture mouse up - this is handy for selecting images..
44382         // perhaps should go somewhere else...
44383         if(!this.editorcore.activated){
44384              this.editor.onFirstFocus();
44385             return;
44386         }
44387         
44388         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44389         // selectNode - might want to handle IE?
44390         if (ev &&
44391             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44392             ev.target && ev.target.tagName == 'IMG') {
44393             // they have click on an image...
44394             // let's see if we can change the selection...
44395             sel = ev.target;
44396          
44397               var nodeRange = sel.ownerDocument.createRange();
44398             try {
44399                 nodeRange.selectNode(sel);
44400             } catch (e) {
44401                 nodeRange.selectNodeContents(sel);
44402             }
44403             //nodeRange.collapse(true);
44404             var s = this.editorcore.win.getSelection();
44405             s.removeAllRanges();
44406             s.addRange(nodeRange);
44407         }  
44408         
44409       
44410         var updateFooter = sel ? false : true;
44411         
44412         
44413         var ans = this.editorcore.getAllAncestors();
44414         
44415         // pick
44416         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44417         
44418         if (!sel) { 
44419             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44420             sel = sel ? sel : this.editorcore.doc.body;
44421             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44422             
44423         }
44424         // pick a menu that exists..
44425         var tn = sel.tagName.toUpperCase();
44426         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44427         
44428         tn = sel.tagName.toUpperCase();
44429         
44430         var lastSel = this.tb.selectedNode
44431         
44432         this.tb.selectedNode = sel;
44433         
44434         // if current menu does not match..
44435         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
44436                 
44437             this.tb.el.hide();
44438             ///console.log("show: " + tn);
44439             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44440             this.tb.el.show();
44441             // update name
44442             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44443             
44444             
44445             // update attributes
44446             if (this.tb.fields) {
44447                 this.tb.fields.each(function(e) {
44448                     if (e.stylename) {
44449                         e.setValue(sel.style[e.stylename]);
44450                         return;
44451                     } 
44452                    e.setValue(sel.getAttribute(e.attrname));
44453                 });
44454             }
44455             
44456             var hasStyles = false;
44457             for(var i in this.styles) {
44458                 hasStyles = true;
44459                 break;
44460             }
44461             
44462             // update styles
44463             if (hasStyles) { 
44464                 var st = this.tb.fields.item(0);
44465                 
44466                 st.store.removeAll();
44467                
44468                 
44469                 var cn = sel.className.split(/\s+/);
44470                 
44471                 var avs = [];
44472                 if (this.styles['*']) {
44473                     
44474                     Roo.each(this.styles['*'], function(v) {
44475                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44476                     });
44477                 }
44478                 if (this.styles[tn]) { 
44479                     Roo.each(this.styles[tn], function(v) {
44480                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44481                     });
44482                 }
44483                 
44484                 st.store.loadData(avs);
44485                 st.collapse();
44486                 st.setValue(cn);
44487             }
44488             // flag our selected Node.
44489             this.tb.selectedNode = sel;
44490            
44491            
44492             Roo.menu.MenuMgr.hideAll();
44493
44494         }
44495         
44496         if (!updateFooter) {
44497             //this.footDisp.dom.innerHTML = ''; 
44498             return;
44499         }
44500         // update the footer
44501         //
44502         var html = '';
44503         
44504         this.footerEls = ans.reverse();
44505         Roo.each(this.footerEls, function(a,i) {
44506             if (!a) { return; }
44507             html += html.length ? ' &gt; '  :  '';
44508             
44509             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44510             
44511         });
44512        
44513         // 
44514         var sz = this.footDisp.up('td').getSize();
44515         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44516         this.footDisp.dom.style.marginLeft = '5px';
44517         
44518         this.footDisp.dom.style.overflow = 'hidden';
44519         
44520         this.footDisp.dom.innerHTML = html;
44521             
44522         //this.editorsyncValue();
44523     },
44524      
44525     
44526    
44527        
44528     // private
44529     onDestroy : function(){
44530         if(this.rendered){
44531             
44532             this.tb.items.each(function(item){
44533                 if(item.menu){
44534                     item.menu.removeAll();
44535                     if(item.menu.el){
44536                         item.menu.el.destroy();
44537                     }
44538                 }
44539                 item.destroy();
44540             });
44541              
44542         }
44543     },
44544     onFirstFocus: function() {
44545         // need to do this for all the toolbars..
44546         this.tb.items.each(function(item){
44547            item.enable();
44548         });
44549     },
44550     buildToolbar: function(tlist, nm)
44551     {
44552         var editor = this.editor;
44553         var editorcore = this.editorcore;
44554          // create a new element.
44555         var wdiv = editor.wrap.createChild({
44556                 tag: 'div'
44557             }, editor.wrap.dom.firstChild.nextSibling, true);
44558         
44559        
44560         var tb = new Roo.Toolbar(wdiv);
44561         // add the name..
44562         
44563         tb.add(nm+ ":&nbsp;");
44564         
44565         var styles = [];
44566         for(var i in this.styles) {
44567             styles.push(i);
44568         }
44569         
44570         // styles...
44571         if (styles && styles.length) {
44572             
44573             // this needs a multi-select checkbox...
44574             tb.addField( new Roo.form.ComboBox({
44575                 store: new Roo.data.SimpleStore({
44576                     id : 'val',
44577                     fields: ['val', 'selected'],
44578                     data : [] 
44579                 }),
44580                 name : '-roo-edit-className',
44581                 attrname : 'className',
44582                 displayField: 'val',
44583                 typeAhead: false,
44584                 mode: 'local',
44585                 editable : false,
44586                 triggerAction: 'all',
44587                 emptyText:'Select Style',
44588                 selectOnFocus:true,
44589                 width: 130,
44590                 listeners : {
44591                     'select': function(c, r, i) {
44592                         // initial support only for on class per el..
44593                         tb.selectedNode.className =  r ? r.get('val') : '';
44594                         editorcore.syncValue();
44595                     }
44596                 }
44597     
44598             }));
44599         }
44600         
44601         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44602         var tbops = tbc.options;
44603         
44604         for (var i in tlist) {
44605             
44606             var item = tlist[i];
44607             tb.add(item.title + ":&nbsp;");
44608             
44609             
44610             //optname == used so you can configure the options available..
44611             var opts = item.opts ? item.opts : false;
44612             if (item.optname) {
44613                 opts = tbops[item.optname];
44614            
44615             }
44616             
44617             if (opts) {
44618                 // opts == pulldown..
44619                 tb.addField( new Roo.form.ComboBox({
44620                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44621                         id : 'val',
44622                         fields: ['val', 'display'],
44623                         data : opts  
44624                     }),
44625                     name : '-roo-edit-' + i,
44626                     attrname : i,
44627                     stylename : item.style ? item.style : false,
44628                     displayField: item.displayField ? item.displayField : 'val',
44629                     valueField :  'val',
44630                     typeAhead: false,
44631                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44632                     editable : false,
44633                     triggerAction: 'all',
44634                     emptyText:'Select',
44635                     selectOnFocus:true,
44636                     width: item.width ? item.width  : 130,
44637                     listeners : {
44638                         'select': function(c, r, i) {
44639                             if (c.stylename) {
44640                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44641                                 return;
44642                             }
44643                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44644                         }
44645                     }
44646
44647                 }));
44648                 continue;
44649                     
44650                  
44651                 
44652                 tb.addField( new Roo.form.TextField({
44653                     name: i,
44654                     width: 100,
44655                     //allowBlank:false,
44656                     value: ''
44657                 }));
44658                 continue;
44659             }
44660             tb.addField( new Roo.form.TextField({
44661                 name: '-roo-edit-' + i,
44662                 attrname : i,
44663                 
44664                 width: item.width,
44665                 //allowBlank:true,
44666                 value: '',
44667                 listeners: {
44668                     'change' : function(f, nv, ov) {
44669                         tb.selectedNode.setAttribute(f.attrname, nv);
44670                     }
44671                 }
44672             }));
44673              
44674         }
44675         
44676         var _this = this;
44677         
44678         if(nm == 'BODY'){
44679             tb.addSeparator();
44680         
44681             tb.addButton( {
44682                 text: 'Stylesheets',
44683
44684                 listeners : {
44685                     click : function ()
44686                     {
44687                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44688                     }
44689                 }
44690             });
44691         }
44692         
44693         tb.addFill();
44694         tb.addButton( {
44695             text: 'Remove Tag',
44696     
44697             listeners : {
44698                 click : function ()
44699                 {
44700                     // remove
44701                     // undo does not work.
44702                      
44703                     var sn = tb.selectedNode;
44704                     
44705                     var pn = sn.parentNode;
44706                     
44707                     var stn =  sn.childNodes[0];
44708                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44709                     while (sn.childNodes.length) {
44710                         var node = sn.childNodes[0];
44711                         sn.removeChild(node);
44712                         //Roo.log(node);
44713                         pn.insertBefore(node, sn);
44714                         
44715                     }
44716                     pn.removeChild(sn);
44717                     var range = editorcore.createRange();
44718         
44719                     range.setStart(stn,0);
44720                     range.setEnd(en,0); //????
44721                     //range.selectNode(sel);
44722                     
44723                     
44724                     var selection = editorcore.getSelection();
44725                     selection.removeAllRanges();
44726                     selection.addRange(range);
44727                     
44728                     
44729                     
44730                     //_this.updateToolbar(null, null, pn);
44731                     _this.updateToolbar(null, null, null);
44732                     _this.footDisp.dom.innerHTML = ''; 
44733                 }
44734             }
44735             
44736                     
44737                 
44738             
44739         });
44740         
44741         
44742         tb.el.on('click', function(e){
44743             e.preventDefault(); // what does this do?
44744         });
44745         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44746         tb.el.hide();
44747         tb.name = nm;
44748         // dont need to disable them... as they will get hidden
44749         return tb;
44750          
44751         
44752     },
44753     buildFooter : function()
44754     {
44755         
44756         var fel = this.editor.wrap.createChild();
44757         this.footer = new Roo.Toolbar(fel);
44758         // toolbar has scrolly on left / right?
44759         var footDisp= new Roo.Toolbar.Fill();
44760         var _t = this;
44761         this.footer.add(
44762             {
44763                 text : '&lt;',
44764                 xtype: 'Button',
44765                 handler : function() {
44766                     _t.footDisp.scrollTo('left',0,true)
44767                 }
44768             }
44769         );
44770         this.footer.add( footDisp );
44771         this.footer.add( 
44772             {
44773                 text : '&gt;',
44774                 xtype: 'Button',
44775                 handler : function() {
44776                     // no animation..
44777                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44778                 }
44779             }
44780         );
44781         var fel = Roo.get(footDisp.el);
44782         fel.addClass('x-editor-context');
44783         this.footDispWrap = fel; 
44784         this.footDispWrap.overflow  = 'hidden';
44785         
44786         this.footDisp = fel.createChild();
44787         this.footDispWrap.on('click', this.onContextClick, this)
44788         
44789         
44790     },
44791     onContextClick : function (ev,dom)
44792     {
44793         ev.preventDefault();
44794         var  cn = dom.className;
44795         //Roo.log(cn);
44796         if (!cn.match(/x-ed-loc-/)) {
44797             return;
44798         }
44799         var n = cn.split('-').pop();
44800         var ans = this.footerEls;
44801         var sel = ans[n];
44802         
44803          // pick
44804         var range = this.editorcore.createRange();
44805         
44806         range.selectNodeContents(sel);
44807         //range.selectNode(sel);
44808         
44809         
44810         var selection = this.editorcore.getSelection();
44811         selection.removeAllRanges();
44812         selection.addRange(range);
44813         
44814         
44815         
44816         this.updateToolbar(null, null, sel);
44817         
44818         
44819     }
44820     
44821     
44822     
44823     
44824     
44825 });
44826
44827
44828
44829
44830
44831 /*
44832  * Based on:
44833  * Ext JS Library 1.1.1
44834  * Copyright(c) 2006-2007, Ext JS, LLC.
44835  *
44836  * Originally Released Under LGPL - original licence link has changed is not relivant.
44837  *
44838  * Fork - LGPL
44839  * <script type="text/javascript">
44840  */
44841  
44842 /**
44843  * @class Roo.form.BasicForm
44844  * @extends Roo.util.Observable
44845  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44846  * @constructor
44847  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44848  * @param {Object} config Configuration options
44849  */
44850 Roo.form.BasicForm = function(el, config){
44851     this.allItems = [];
44852     this.childForms = [];
44853     Roo.apply(this, config);
44854     /*
44855      * The Roo.form.Field items in this form.
44856      * @type MixedCollection
44857      */
44858      
44859      
44860     this.items = new Roo.util.MixedCollection(false, function(o){
44861         return o.id || (o.id = Roo.id());
44862     });
44863     this.addEvents({
44864         /**
44865          * @event beforeaction
44866          * Fires before any action is performed. Return false to cancel the action.
44867          * @param {Form} this
44868          * @param {Action} action The action to be performed
44869          */
44870         beforeaction: true,
44871         /**
44872          * @event actionfailed
44873          * Fires when an action fails.
44874          * @param {Form} this
44875          * @param {Action} action The action that failed
44876          */
44877         actionfailed : true,
44878         /**
44879          * @event actioncomplete
44880          * Fires when an action is completed.
44881          * @param {Form} this
44882          * @param {Action} action The action that completed
44883          */
44884         actioncomplete : true
44885     });
44886     if(el){
44887         this.initEl(el);
44888     }
44889     Roo.form.BasicForm.superclass.constructor.call(this);
44890 };
44891
44892 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44893     /**
44894      * @cfg {String} method
44895      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44896      */
44897     /**
44898      * @cfg {DataReader} reader
44899      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44900      * This is optional as there is built-in support for processing JSON.
44901      */
44902     /**
44903      * @cfg {DataReader} errorReader
44904      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44905      * This is completely optional as there is built-in support for processing JSON.
44906      */
44907     /**
44908      * @cfg {String} url
44909      * The URL to use for form actions if one isn't supplied in the action options.
44910      */
44911     /**
44912      * @cfg {Boolean} fileUpload
44913      * Set to true if this form is a file upload.
44914      */
44915      
44916     /**
44917      * @cfg {Object} baseParams
44918      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44919      */
44920      /**
44921      
44922     /**
44923      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44924      */
44925     timeout: 30,
44926
44927     // private
44928     activeAction : null,
44929
44930     /**
44931      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44932      * or setValues() data instead of when the form was first created.
44933      */
44934     trackResetOnLoad : false,
44935     
44936     
44937     /**
44938      * childForms - used for multi-tab forms
44939      * @type {Array}
44940      */
44941     childForms : false,
44942     
44943     /**
44944      * allItems - full list of fields.
44945      * @type {Array}
44946      */
44947     allItems : false,
44948     
44949     /**
44950      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44951      * element by passing it or its id or mask the form itself by passing in true.
44952      * @type Mixed
44953      */
44954     waitMsgTarget : false,
44955
44956     // private
44957     initEl : function(el){
44958         this.el = Roo.get(el);
44959         this.id = this.el.id || Roo.id();
44960         this.el.on('submit', this.onSubmit, this);
44961         this.el.addClass('x-form');
44962     },
44963
44964     // private
44965     onSubmit : function(e){
44966         e.stopEvent();
44967     },
44968
44969     /**
44970      * Returns true if client-side validation on the form is successful.
44971      * @return Boolean
44972      */
44973     isValid : function(){
44974         var valid = true;
44975         this.items.each(function(f){
44976            if(!f.validate()){
44977                valid = false;
44978            }
44979         });
44980         return valid;
44981     },
44982
44983     /**
44984      * Returns true if any fields in this form have changed since their original load.
44985      * @return Boolean
44986      */
44987     isDirty : function(){
44988         var dirty = false;
44989         this.items.each(function(f){
44990            if(f.isDirty()){
44991                dirty = true;
44992                return false;
44993            }
44994         });
44995         return dirty;
44996     },
44997
44998     /**
44999      * Performs a predefined action (submit or load) or custom actions you define on this form.
45000      * @param {String} actionName The name of the action type
45001      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45002      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45003      * accept other config options):
45004      * <pre>
45005 Property          Type             Description
45006 ----------------  ---------------  ----------------------------------------------------------------------------------
45007 url               String           The url for the action (defaults to the form's url)
45008 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45009 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45010 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45011                                    validate the form on the client (defaults to false)
45012      * </pre>
45013      * @return {BasicForm} this
45014      */
45015     doAction : function(action, options){
45016         if(typeof action == 'string'){
45017             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45018         }
45019         if(this.fireEvent('beforeaction', this, action) !== false){
45020             this.beforeAction(action);
45021             action.run.defer(100, action);
45022         }
45023         return this;
45024     },
45025
45026     /**
45027      * Shortcut to do a submit action.
45028      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45029      * @return {BasicForm} this
45030      */
45031     submit : function(options){
45032         this.doAction('submit', options);
45033         return this;
45034     },
45035
45036     /**
45037      * Shortcut to do a load action.
45038      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45039      * @return {BasicForm} this
45040      */
45041     load : function(options){
45042         this.doAction('load', options);
45043         return this;
45044     },
45045
45046     /**
45047      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45048      * @param {Record} record The record to edit
45049      * @return {BasicForm} this
45050      */
45051     updateRecord : function(record){
45052         record.beginEdit();
45053         var fs = record.fields;
45054         fs.each(function(f){
45055             var field = this.findField(f.name);
45056             if(field){
45057                 record.set(f.name, field.getValue());
45058             }
45059         }, this);
45060         record.endEdit();
45061         return this;
45062     },
45063
45064     /**
45065      * Loads an Roo.data.Record into this form.
45066      * @param {Record} record The record to load
45067      * @return {BasicForm} this
45068      */
45069     loadRecord : function(record){
45070         this.setValues(record.data);
45071         return this;
45072     },
45073
45074     // private
45075     beforeAction : function(action){
45076         var o = action.options;
45077         
45078        
45079         if(this.waitMsgTarget === true){
45080             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45081         }else if(this.waitMsgTarget){
45082             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45083             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45084         }else {
45085             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45086         }
45087          
45088     },
45089
45090     // private
45091     afterAction : function(action, success){
45092         this.activeAction = null;
45093         var o = action.options;
45094         
45095         if(this.waitMsgTarget === true){
45096             this.el.unmask();
45097         }else if(this.waitMsgTarget){
45098             this.waitMsgTarget.unmask();
45099         }else{
45100             Roo.MessageBox.updateProgress(1);
45101             Roo.MessageBox.hide();
45102         }
45103          
45104         if(success){
45105             if(o.reset){
45106                 this.reset();
45107             }
45108             Roo.callback(o.success, o.scope, [this, action]);
45109             this.fireEvent('actioncomplete', this, action);
45110             
45111         }else{
45112             
45113             // failure condition..
45114             // we have a scenario where updates need confirming.
45115             // eg. if a locking scenario exists..
45116             // we look for { errors : { needs_confirm : true }} in the response.
45117             if (
45118                 (typeof(action.result) != 'undefined')  &&
45119                 (typeof(action.result.errors) != 'undefined')  &&
45120                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45121            ){
45122                 var _t = this;
45123                 Roo.MessageBox.confirm(
45124                     "Change requires confirmation",
45125                     action.result.errorMsg,
45126                     function(r) {
45127                         if (r != 'yes') {
45128                             return;
45129                         }
45130                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45131                     }
45132                     
45133                 );
45134                 
45135                 
45136                 
45137                 return;
45138             }
45139             
45140             Roo.callback(o.failure, o.scope, [this, action]);
45141             // show an error message if no failed handler is set..
45142             if (!this.hasListener('actionfailed')) {
45143                 Roo.MessageBox.alert("Error",
45144                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45145                         action.result.errorMsg :
45146                         "Saving Failed, please check your entries or try again"
45147                 );
45148             }
45149             
45150             this.fireEvent('actionfailed', this, action);
45151         }
45152         
45153     },
45154
45155     /**
45156      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45157      * @param {String} id The value to search for
45158      * @return Field
45159      */
45160     findField : function(id){
45161         var field = this.items.get(id);
45162         if(!field){
45163             this.items.each(function(f){
45164                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45165                     field = f;
45166                     return false;
45167                 }
45168             });
45169         }
45170         return field || null;
45171     },
45172
45173     /**
45174      * Add a secondary form to this one, 
45175      * Used to provide tabbed forms. One form is primary, with hidden values 
45176      * which mirror the elements from the other forms.
45177      * 
45178      * @param {Roo.form.Form} form to add.
45179      * 
45180      */
45181     addForm : function(form)
45182     {
45183        
45184         if (this.childForms.indexOf(form) > -1) {
45185             // already added..
45186             return;
45187         }
45188         this.childForms.push(form);
45189         var n = '';
45190         Roo.each(form.allItems, function (fe) {
45191             
45192             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45193             if (this.findField(n)) { // already added..
45194                 return;
45195             }
45196             var add = new Roo.form.Hidden({
45197                 name : n
45198             });
45199             add.render(this.el);
45200             
45201             this.add( add );
45202         }, this);
45203         
45204     },
45205     /**
45206      * Mark fields in this form invalid in bulk.
45207      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45208      * @return {BasicForm} this
45209      */
45210     markInvalid : function(errors){
45211         if(errors instanceof Array){
45212             for(var i = 0, len = errors.length; i < len; i++){
45213                 var fieldError = errors[i];
45214                 var f = this.findField(fieldError.id);
45215                 if(f){
45216                     f.markInvalid(fieldError.msg);
45217                 }
45218             }
45219         }else{
45220             var field, id;
45221             for(id in errors){
45222                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45223                     field.markInvalid(errors[id]);
45224                 }
45225             }
45226         }
45227         Roo.each(this.childForms || [], function (f) {
45228             f.markInvalid(errors);
45229         });
45230         
45231         return this;
45232     },
45233
45234     /**
45235      * Set values for fields in this form in bulk.
45236      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45237      * @return {BasicForm} this
45238      */
45239     setValues : function(values){
45240         if(values instanceof Array){ // array of objects
45241             for(var i = 0, len = values.length; i < len; i++){
45242                 var v = values[i];
45243                 var f = this.findField(v.id);
45244                 if(f){
45245                     f.setValue(v.value);
45246                     if(this.trackResetOnLoad){
45247                         f.originalValue = f.getValue();
45248                     }
45249                 }
45250             }
45251         }else{ // object hash
45252             var field, id;
45253             for(id in values){
45254                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45255                     
45256                     if (field.setFromData && 
45257                         field.valueField && 
45258                         field.displayField &&
45259                         // combos' with local stores can 
45260                         // be queried via setValue()
45261                         // to set their value..
45262                         (field.store && !field.store.isLocal)
45263                         ) {
45264                         // it's a combo
45265                         var sd = { };
45266                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45267                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45268                         field.setFromData(sd);
45269                         
45270                     } else {
45271                         field.setValue(values[id]);
45272                     }
45273                     
45274                     
45275                     if(this.trackResetOnLoad){
45276                         field.originalValue = field.getValue();
45277                     }
45278                 }
45279             }
45280         }
45281          
45282         Roo.each(this.childForms || [], function (f) {
45283             f.setValues(values);
45284         });
45285                 
45286         return this;
45287     },
45288
45289     /**
45290      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45291      * they are returned as an array.
45292      * @param {Boolean} asString
45293      * @return {Object}
45294      */
45295     getValues : function(asString){
45296         if (this.childForms) {
45297             // copy values from the child forms
45298             Roo.each(this.childForms, function (f) {
45299                 this.setValues(f.getValues());
45300             }, this);
45301         }
45302         
45303         
45304         
45305         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45306         if(asString === true){
45307             return fs;
45308         }
45309         return Roo.urlDecode(fs);
45310     },
45311     
45312     /**
45313      * Returns the fields in this form as an object with key/value pairs. 
45314      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45315      * @return {Object}
45316      */
45317     getFieldValues : function(with_hidden)
45318     {
45319         if (this.childForms) {
45320             // copy values from the child forms
45321             // should this call getFieldValues - probably not as we do not currently copy
45322             // hidden fields when we generate..
45323             Roo.each(this.childForms, function (f) {
45324                 this.setValues(f.getValues());
45325             }, this);
45326         }
45327         
45328         var ret = {};
45329         this.items.each(function(f){
45330             if (!f.getName()) {
45331                 return;
45332             }
45333             var v = f.getValue();
45334             if (f.inputType =='radio') {
45335                 if (typeof(ret[f.getName()]) == 'undefined') {
45336                     ret[f.getName()] = ''; // empty..
45337                 }
45338                 
45339                 if (!f.el.dom.checked) {
45340                     return;
45341                     
45342                 }
45343                 v = f.el.dom.value;
45344                 
45345             }
45346             
45347             // not sure if this supported any more..
45348             if ((typeof(v) == 'object') && f.getRawValue) {
45349                 v = f.getRawValue() ; // dates..
45350             }
45351             // combo boxes where name != hiddenName...
45352             if (f.name != f.getName()) {
45353                 ret[f.name] = f.getRawValue();
45354             }
45355             ret[f.getName()] = v;
45356         });
45357         
45358         return ret;
45359     },
45360
45361     /**
45362      * Clears all invalid messages in this form.
45363      * @return {BasicForm} this
45364      */
45365     clearInvalid : function(){
45366         this.items.each(function(f){
45367            f.clearInvalid();
45368         });
45369         
45370         Roo.each(this.childForms || [], function (f) {
45371             f.clearInvalid();
45372         });
45373         
45374         
45375         return this;
45376     },
45377
45378     /**
45379      * Resets this form.
45380      * @return {BasicForm} this
45381      */
45382     reset : function(){
45383         this.items.each(function(f){
45384             f.reset();
45385         });
45386         
45387         Roo.each(this.childForms || [], function (f) {
45388             f.reset();
45389         });
45390        
45391         
45392         return this;
45393     },
45394
45395     /**
45396      * Add Roo.form components to this form.
45397      * @param {Field} field1
45398      * @param {Field} field2 (optional)
45399      * @param {Field} etc (optional)
45400      * @return {BasicForm} this
45401      */
45402     add : function(){
45403         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45404         return this;
45405     },
45406
45407
45408     /**
45409      * Removes a field from the items collection (does NOT remove its markup).
45410      * @param {Field} field
45411      * @return {BasicForm} this
45412      */
45413     remove : function(field){
45414         this.items.remove(field);
45415         return this;
45416     },
45417
45418     /**
45419      * Looks at the fields in this form, checks them for an id attribute,
45420      * and calls applyTo on the existing dom element with that id.
45421      * @return {BasicForm} this
45422      */
45423     render : function(){
45424         this.items.each(function(f){
45425             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45426                 f.applyTo(f.id);
45427             }
45428         });
45429         return this;
45430     },
45431
45432     /**
45433      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45434      * @param {Object} values
45435      * @return {BasicForm} this
45436      */
45437     applyToFields : function(o){
45438         this.items.each(function(f){
45439            Roo.apply(f, o);
45440         });
45441         return this;
45442     },
45443
45444     /**
45445      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45446      * @param {Object} values
45447      * @return {BasicForm} this
45448      */
45449     applyIfToFields : function(o){
45450         this.items.each(function(f){
45451            Roo.applyIf(f, o);
45452         });
45453         return this;
45454     }
45455 });
45456
45457 // back compat
45458 Roo.BasicForm = Roo.form.BasicForm;/*
45459  * Based on:
45460  * Ext JS Library 1.1.1
45461  * Copyright(c) 2006-2007, Ext JS, LLC.
45462  *
45463  * Originally Released Under LGPL - original licence link has changed is not relivant.
45464  *
45465  * Fork - LGPL
45466  * <script type="text/javascript">
45467  */
45468
45469 /**
45470  * @class Roo.form.Form
45471  * @extends Roo.form.BasicForm
45472  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45473  * @constructor
45474  * @param {Object} config Configuration options
45475  */
45476 Roo.form.Form = function(config){
45477     var xitems =  [];
45478     if (config.items) {
45479         xitems = config.items;
45480         delete config.items;
45481     }
45482    
45483     
45484     Roo.form.Form.superclass.constructor.call(this, null, config);
45485     this.url = this.url || this.action;
45486     if(!this.root){
45487         this.root = new Roo.form.Layout(Roo.applyIf({
45488             id: Roo.id()
45489         }, config));
45490     }
45491     this.active = this.root;
45492     /**
45493      * Array of all the buttons that have been added to this form via {@link addButton}
45494      * @type Array
45495      */
45496     this.buttons = [];
45497     this.allItems = [];
45498     this.addEvents({
45499         /**
45500          * @event clientvalidation
45501          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45502          * @param {Form} this
45503          * @param {Boolean} valid true if the form has passed client-side validation
45504          */
45505         clientvalidation: true,
45506         /**
45507          * @event rendered
45508          * Fires when the form is rendered
45509          * @param {Roo.form.Form} form
45510          */
45511         rendered : true
45512     });
45513     
45514     if (this.progressUrl) {
45515             // push a hidden field onto the list of fields..
45516             this.addxtype( {
45517                     xns: Roo.form, 
45518                     xtype : 'Hidden', 
45519                     name : 'UPLOAD_IDENTIFIER' 
45520             });
45521         }
45522         
45523     
45524     Roo.each(xitems, this.addxtype, this);
45525     
45526     
45527     
45528 };
45529
45530 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45531     /**
45532      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45533      */
45534     /**
45535      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45536      */
45537     /**
45538      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45539      */
45540     buttonAlign:'center',
45541
45542     /**
45543      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45544      */
45545     minButtonWidth:75,
45546
45547     /**
45548      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45549      * This property cascades to child containers if not set.
45550      */
45551     labelAlign:'left',
45552
45553     /**
45554      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45555      * fires a looping event with that state. This is required to bind buttons to the valid
45556      * state using the config value formBind:true on the button.
45557      */
45558     monitorValid : false,
45559
45560     /**
45561      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45562      */
45563     monitorPoll : 200,
45564     
45565     /**
45566      * @cfg {String} progressUrl - Url to return progress data 
45567      */
45568     
45569     progressUrl : false,
45570   
45571     /**
45572      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45573      * fields are added and the column is closed. If no fields are passed the column remains open
45574      * until end() is called.
45575      * @param {Object} config The config to pass to the column
45576      * @param {Field} field1 (optional)
45577      * @param {Field} field2 (optional)
45578      * @param {Field} etc (optional)
45579      * @return Column The column container object
45580      */
45581     column : function(c){
45582         var col = new Roo.form.Column(c);
45583         this.start(col);
45584         if(arguments.length > 1){ // duplicate code required because of Opera
45585             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45586             this.end();
45587         }
45588         return col;
45589     },
45590
45591     /**
45592      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45593      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45594      * until end() is called.
45595      * @param {Object} config The config to pass to the fieldset
45596      * @param {Field} field1 (optional)
45597      * @param {Field} field2 (optional)
45598      * @param {Field} etc (optional)
45599      * @return FieldSet The fieldset container object
45600      */
45601     fieldset : function(c){
45602         var fs = new Roo.form.FieldSet(c);
45603         this.start(fs);
45604         if(arguments.length > 1){ // duplicate code required because of Opera
45605             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45606             this.end();
45607         }
45608         return fs;
45609     },
45610
45611     /**
45612      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45613      * fields are added and the container is closed. If no fields are passed the container remains open
45614      * until end() is called.
45615      * @param {Object} config The config to pass to the Layout
45616      * @param {Field} field1 (optional)
45617      * @param {Field} field2 (optional)
45618      * @param {Field} etc (optional)
45619      * @return Layout The container object
45620      */
45621     container : function(c){
45622         var l = new Roo.form.Layout(c);
45623         this.start(l);
45624         if(arguments.length > 1){ // duplicate code required because of Opera
45625             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45626             this.end();
45627         }
45628         return l;
45629     },
45630
45631     /**
45632      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45633      * @param {Object} container A Roo.form.Layout or subclass of Layout
45634      * @return {Form} this
45635      */
45636     start : function(c){
45637         // cascade label info
45638         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45639         this.active.stack.push(c);
45640         c.ownerCt = this.active;
45641         this.active = c;
45642         return this;
45643     },
45644
45645     /**
45646      * Closes the current open container
45647      * @return {Form} this
45648      */
45649     end : function(){
45650         if(this.active == this.root){
45651             return this;
45652         }
45653         this.active = this.active.ownerCt;
45654         return this;
45655     },
45656
45657     /**
45658      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45659      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45660      * as the label of the field.
45661      * @param {Field} field1
45662      * @param {Field} field2 (optional)
45663      * @param {Field} etc. (optional)
45664      * @return {Form} this
45665      */
45666     add : function(){
45667         this.active.stack.push.apply(this.active.stack, arguments);
45668         this.allItems.push.apply(this.allItems,arguments);
45669         var r = [];
45670         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45671             if(a[i].isFormField){
45672                 r.push(a[i]);
45673             }
45674         }
45675         if(r.length > 0){
45676             Roo.form.Form.superclass.add.apply(this, r);
45677         }
45678         return this;
45679     },
45680     
45681
45682     
45683     
45684     
45685      /**
45686      * Find any element that has been added to a form, using it's ID or name
45687      * This can include framesets, columns etc. along with regular fields..
45688      * @param {String} id - id or name to find.
45689      
45690      * @return {Element} e - or false if nothing found.
45691      */
45692     findbyId : function(id)
45693     {
45694         var ret = false;
45695         if (!id) {
45696             return ret;
45697         }
45698         Roo.each(this.allItems, function(f){
45699             if (f.id == id || f.name == id ){
45700                 ret = f;
45701                 return false;
45702             }
45703         });
45704         return ret;
45705     },
45706
45707     
45708     
45709     /**
45710      * Render this form into the passed container. This should only be called once!
45711      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45712      * @return {Form} this
45713      */
45714     render : function(ct)
45715     {
45716         
45717         
45718         
45719         ct = Roo.get(ct);
45720         var o = this.autoCreate || {
45721             tag: 'form',
45722             method : this.method || 'POST',
45723             id : this.id || Roo.id()
45724         };
45725         this.initEl(ct.createChild(o));
45726
45727         this.root.render(this.el);
45728         
45729        
45730              
45731         this.items.each(function(f){
45732             f.render('x-form-el-'+f.id);
45733         });
45734
45735         if(this.buttons.length > 0){
45736             // tables are required to maintain order and for correct IE layout
45737             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45738                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45739                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45740             }}, null, true);
45741             var tr = tb.getElementsByTagName('tr')[0];
45742             for(var i = 0, len = this.buttons.length; i < len; i++) {
45743                 var b = this.buttons[i];
45744                 var td = document.createElement('td');
45745                 td.className = 'x-form-btn-td';
45746                 b.render(tr.appendChild(td));
45747             }
45748         }
45749         if(this.monitorValid){ // initialize after render
45750             this.startMonitoring();
45751         }
45752         this.fireEvent('rendered', this);
45753         return this;
45754     },
45755
45756     /**
45757      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45758      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45759      * object or a valid Roo.DomHelper element config
45760      * @param {Function} handler The function called when the button is clicked
45761      * @param {Object} scope (optional) The scope of the handler function
45762      * @return {Roo.Button}
45763      */
45764     addButton : function(config, handler, scope){
45765         var bc = {
45766             handler: handler,
45767             scope: scope,
45768             minWidth: this.minButtonWidth,
45769             hideParent:true
45770         };
45771         if(typeof config == "string"){
45772             bc.text = config;
45773         }else{
45774             Roo.apply(bc, config);
45775         }
45776         var btn = new Roo.Button(null, bc);
45777         this.buttons.push(btn);
45778         return btn;
45779     },
45780
45781      /**
45782      * Adds a series of form elements (using the xtype property as the factory method.
45783      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45784      * @param {Object} config 
45785      */
45786     
45787     addxtype : function()
45788     {
45789         var ar = Array.prototype.slice.call(arguments, 0);
45790         var ret = false;
45791         for(var i = 0; i < ar.length; i++) {
45792             if (!ar[i]) {
45793                 continue; // skip -- if this happends something invalid got sent, we 
45794                 // should ignore it, as basically that interface element will not show up
45795                 // and that should be pretty obvious!!
45796             }
45797             
45798             if (Roo.form[ar[i].xtype]) {
45799                 ar[i].form = this;
45800                 var fe = Roo.factory(ar[i], Roo.form);
45801                 if (!ret) {
45802                     ret = fe;
45803                 }
45804                 fe.form = this;
45805                 if (fe.store) {
45806                     fe.store.form = this;
45807                 }
45808                 if (fe.isLayout) {  
45809                          
45810                     this.start(fe);
45811                     this.allItems.push(fe);
45812                     if (fe.items && fe.addxtype) {
45813                         fe.addxtype.apply(fe, fe.items);
45814                         delete fe.items;
45815                     }
45816                      this.end();
45817                     continue;
45818                 }
45819                 
45820                 
45821                  
45822                 this.add(fe);
45823               //  console.log('adding ' + ar[i].xtype);
45824             }
45825             if (ar[i].xtype == 'Button') {  
45826                 //console.log('adding button');
45827                 //console.log(ar[i]);
45828                 this.addButton(ar[i]);
45829                 this.allItems.push(fe);
45830                 continue;
45831             }
45832             
45833             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45834                 alert('end is not supported on xtype any more, use items');
45835             //    this.end();
45836             //    //console.log('adding end');
45837             }
45838             
45839         }
45840         return ret;
45841     },
45842     
45843     /**
45844      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45845      * option "monitorValid"
45846      */
45847     startMonitoring : function(){
45848         if(!this.bound){
45849             this.bound = true;
45850             Roo.TaskMgr.start({
45851                 run : this.bindHandler,
45852                 interval : this.monitorPoll || 200,
45853                 scope: this
45854             });
45855         }
45856     },
45857
45858     /**
45859      * Stops monitoring of the valid state of this form
45860      */
45861     stopMonitoring : function(){
45862         this.bound = false;
45863     },
45864
45865     // private
45866     bindHandler : function(){
45867         if(!this.bound){
45868             return false; // stops binding
45869         }
45870         var valid = true;
45871         this.items.each(function(f){
45872             if(!f.isValid(true)){
45873                 valid = false;
45874                 return false;
45875             }
45876         });
45877         for(var i = 0, len = this.buttons.length; i < len; i++){
45878             var btn = this.buttons[i];
45879             if(btn.formBind === true && btn.disabled === valid){
45880                 btn.setDisabled(!valid);
45881             }
45882         }
45883         this.fireEvent('clientvalidation', this, valid);
45884     }
45885     
45886     
45887     
45888     
45889     
45890     
45891     
45892     
45893 });
45894
45895
45896 // back compat
45897 Roo.Form = Roo.form.Form;
45898 /*
45899  * Based on:
45900  * Ext JS Library 1.1.1
45901  * Copyright(c) 2006-2007, Ext JS, LLC.
45902  *
45903  * Originally Released Under LGPL - original licence link has changed is not relivant.
45904  *
45905  * Fork - LGPL
45906  * <script type="text/javascript">
45907  */
45908
45909 // as we use this in bootstrap.
45910 Roo.namespace('Roo.form');
45911  /**
45912  * @class Roo.form.Action
45913  * Internal Class used to handle form actions
45914  * @constructor
45915  * @param {Roo.form.BasicForm} el The form element or its id
45916  * @param {Object} config Configuration options
45917  */
45918
45919  
45920  
45921 // define the action interface
45922 Roo.form.Action = function(form, options){
45923     this.form = form;
45924     this.options = options || {};
45925 };
45926 /**
45927  * Client Validation Failed
45928  * @const 
45929  */
45930 Roo.form.Action.CLIENT_INVALID = 'client';
45931 /**
45932  * Server Validation Failed
45933  * @const 
45934  */
45935 Roo.form.Action.SERVER_INVALID = 'server';
45936  /**
45937  * Connect to Server Failed
45938  * @const 
45939  */
45940 Roo.form.Action.CONNECT_FAILURE = 'connect';
45941 /**
45942  * Reading Data from Server Failed
45943  * @const 
45944  */
45945 Roo.form.Action.LOAD_FAILURE = 'load';
45946
45947 Roo.form.Action.prototype = {
45948     type : 'default',
45949     failureType : undefined,
45950     response : undefined,
45951     result : undefined,
45952
45953     // interface method
45954     run : function(options){
45955
45956     },
45957
45958     // interface method
45959     success : function(response){
45960
45961     },
45962
45963     // interface method
45964     handleResponse : function(response){
45965
45966     },
45967
45968     // default connection failure
45969     failure : function(response){
45970         
45971         this.response = response;
45972         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45973         this.form.afterAction(this, false);
45974     },
45975
45976     processResponse : function(response){
45977         this.response = response;
45978         if(!response.responseText){
45979             return true;
45980         }
45981         this.result = this.handleResponse(response);
45982         return this.result;
45983     },
45984
45985     // utility functions used internally
45986     getUrl : function(appendParams){
45987         var url = this.options.url || this.form.url || this.form.el.dom.action;
45988         if(appendParams){
45989             var p = this.getParams();
45990             if(p){
45991                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
45992             }
45993         }
45994         return url;
45995     },
45996
45997     getMethod : function(){
45998         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
45999     },
46000
46001     getParams : function(){
46002         var bp = this.form.baseParams;
46003         var p = this.options.params;
46004         if(p){
46005             if(typeof p == "object"){
46006                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46007             }else if(typeof p == 'string' && bp){
46008                 p += '&' + Roo.urlEncode(bp);
46009             }
46010         }else if(bp){
46011             p = Roo.urlEncode(bp);
46012         }
46013         return p;
46014     },
46015
46016     createCallback : function(){
46017         return {
46018             success: this.success,
46019             failure: this.failure,
46020             scope: this,
46021             timeout: (this.form.timeout*1000),
46022             upload: this.form.fileUpload ? this.success : undefined
46023         };
46024     }
46025 };
46026
46027 Roo.form.Action.Submit = function(form, options){
46028     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46029 };
46030
46031 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46032     type : 'submit',
46033
46034     haveProgress : false,
46035     uploadComplete : false,
46036     
46037     // uploadProgress indicator.
46038     uploadProgress : function()
46039     {
46040         if (!this.form.progressUrl) {
46041             return;
46042         }
46043         
46044         if (!this.haveProgress) {
46045             Roo.MessageBox.progress("Uploading", "Uploading");
46046         }
46047         if (this.uploadComplete) {
46048            Roo.MessageBox.hide();
46049            return;
46050         }
46051         
46052         this.haveProgress = true;
46053    
46054         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46055         
46056         var c = new Roo.data.Connection();
46057         c.request({
46058             url : this.form.progressUrl,
46059             params: {
46060                 id : uid
46061             },
46062             method: 'GET',
46063             success : function(req){
46064                //console.log(data);
46065                 var rdata = false;
46066                 var edata;
46067                 try  {
46068                    rdata = Roo.decode(req.responseText)
46069                 } catch (e) {
46070                     Roo.log("Invalid data from server..");
46071                     Roo.log(edata);
46072                     return;
46073                 }
46074                 if (!rdata || !rdata.success) {
46075                     Roo.log(rdata);
46076                     Roo.MessageBox.alert(Roo.encode(rdata));
46077                     return;
46078                 }
46079                 var data = rdata.data;
46080                 
46081                 if (this.uploadComplete) {
46082                    Roo.MessageBox.hide();
46083                    return;
46084                 }
46085                    
46086                 if (data){
46087                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46088                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46089                     );
46090                 }
46091                 this.uploadProgress.defer(2000,this);
46092             },
46093        
46094             failure: function(data) {
46095                 Roo.log('progress url failed ');
46096                 Roo.log(data);
46097             },
46098             scope : this
46099         });
46100            
46101     },
46102     
46103     
46104     run : function()
46105     {
46106         // run get Values on the form, so it syncs any secondary forms.
46107         this.form.getValues();
46108         
46109         var o = this.options;
46110         var method = this.getMethod();
46111         var isPost = method == 'POST';
46112         if(o.clientValidation === false || this.form.isValid()){
46113             
46114             if (this.form.progressUrl) {
46115                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46116                     (new Date() * 1) + '' + Math.random());
46117                     
46118             } 
46119             
46120             
46121             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46122                 form:this.form.el.dom,
46123                 url:this.getUrl(!isPost),
46124                 method: method,
46125                 params:isPost ? this.getParams() : null,
46126                 isUpload: this.form.fileUpload
46127             }));
46128             
46129             this.uploadProgress();
46130
46131         }else if (o.clientValidation !== false){ // client validation failed
46132             this.failureType = Roo.form.Action.CLIENT_INVALID;
46133             this.form.afterAction(this, false);
46134         }
46135     },
46136
46137     success : function(response)
46138     {
46139         this.uploadComplete= true;
46140         if (this.haveProgress) {
46141             Roo.MessageBox.hide();
46142         }
46143         
46144         
46145         var result = this.processResponse(response);
46146         if(result === true || result.success){
46147             this.form.afterAction(this, true);
46148             return;
46149         }
46150         if(result.errors){
46151             this.form.markInvalid(result.errors);
46152             this.failureType = Roo.form.Action.SERVER_INVALID;
46153         }
46154         this.form.afterAction(this, false);
46155     },
46156     failure : function(response)
46157     {
46158         this.uploadComplete= true;
46159         if (this.haveProgress) {
46160             Roo.MessageBox.hide();
46161         }
46162         
46163         this.response = response;
46164         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46165         this.form.afterAction(this, false);
46166     },
46167     
46168     handleResponse : function(response){
46169         if(this.form.errorReader){
46170             var rs = this.form.errorReader.read(response);
46171             var errors = [];
46172             if(rs.records){
46173                 for(var i = 0, len = rs.records.length; i < len; i++) {
46174                     var r = rs.records[i];
46175                     errors[i] = r.data;
46176                 }
46177             }
46178             if(errors.length < 1){
46179                 errors = null;
46180             }
46181             return {
46182                 success : rs.success,
46183                 errors : errors
46184             };
46185         }
46186         var ret = false;
46187         try {
46188             ret = Roo.decode(response.responseText);
46189         } catch (e) {
46190             ret = {
46191                 success: false,
46192                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46193                 errors : []
46194             };
46195         }
46196         return ret;
46197         
46198     }
46199 });
46200
46201
46202 Roo.form.Action.Load = function(form, options){
46203     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46204     this.reader = this.form.reader;
46205 };
46206
46207 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46208     type : 'load',
46209
46210     run : function(){
46211         
46212         Roo.Ajax.request(Roo.apply(
46213                 this.createCallback(), {
46214                     method:this.getMethod(),
46215                     url:this.getUrl(false),
46216                     params:this.getParams()
46217         }));
46218     },
46219
46220     success : function(response){
46221         
46222         var result = this.processResponse(response);
46223         if(result === true || !result.success || !result.data){
46224             this.failureType = Roo.form.Action.LOAD_FAILURE;
46225             this.form.afterAction(this, false);
46226             return;
46227         }
46228         this.form.clearInvalid();
46229         this.form.setValues(result.data);
46230         this.form.afterAction(this, true);
46231     },
46232
46233     handleResponse : function(response){
46234         if(this.form.reader){
46235             var rs = this.form.reader.read(response);
46236             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46237             return {
46238                 success : rs.success,
46239                 data : data
46240             };
46241         }
46242         return Roo.decode(response.responseText);
46243     }
46244 });
46245
46246 Roo.form.Action.ACTION_TYPES = {
46247     'load' : Roo.form.Action.Load,
46248     'submit' : Roo.form.Action.Submit
46249 };/*
46250  * Based on:
46251  * Ext JS Library 1.1.1
46252  * Copyright(c) 2006-2007, Ext JS, LLC.
46253  *
46254  * Originally Released Under LGPL - original licence link has changed is not relivant.
46255  *
46256  * Fork - LGPL
46257  * <script type="text/javascript">
46258  */
46259  
46260 /**
46261  * @class Roo.form.Layout
46262  * @extends Roo.Component
46263  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46264  * @constructor
46265  * @param {Object} config Configuration options
46266  */
46267 Roo.form.Layout = function(config){
46268     var xitems = [];
46269     if (config.items) {
46270         xitems = config.items;
46271         delete config.items;
46272     }
46273     Roo.form.Layout.superclass.constructor.call(this, config);
46274     this.stack = [];
46275     Roo.each(xitems, this.addxtype, this);
46276      
46277 };
46278
46279 Roo.extend(Roo.form.Layout, Roo.Component, {
46280     /**
46281      * @cfg {String/Object} autoCreate
46282      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46283      */
46284     /**
46285      * @cfg {String/Object/Function} style
46286      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46287      * a function which returns such a specification.
46288      */
46289     /**
46290      * @cfg {String} labelAlign
46291      * Valid values are "left," "top" and "right" (defaults to "left")
46292      */
46293     /**
46294      * @cfg {Number} labelWidth
46295      * Fixed width in pixels of all field labels (defaults to undefined)
46296      */
46297     /**
46298      * @cfg {Boolean} clear
46299      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46300      */
46301     clear : true,
46302     /**
46303      * @cfg {String} labelSeparator
46304      * The separator to use after field labels (defaults to ':')
46305      */
46306     labelSeparator : ':',
46307     /**
46308      * @cfg {Boolean} hideLabels
46309      * True to suppress the display of field labels in this layout (defaults to false)
46310      */
46311     hideLabels : false,
46312
46313     // private
46314     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46315     
46316     isLayout : true,
46317     
46318     // private
46319     onRender : function(ct, position){
46320         if(this.el){ // from markup
46321             this.el = Roo.get(this.el);
46322         }else {  // generate
46323             var cfg = this.getAutoCreate();
46324             this.el = ct.createChild(cfg, position);
46325         }
46326         if(this.style){
46327             this.el.applyStyles(this.style);
46328         }
46329         if(this.labelAlign){
46330             this.el.addClass('x-form-label-'+this.labelAlign);
46331         }
46332         if(this.hideLabels){
46333             this.labelStyle = "display:none";
46334             this.elementStyle = "padding-left:0;";
46335         }else{
46336             if(typeof this.labelWidth == 'number'){
46337                 this.labelStyle = "width:"+this.labelWidth+"px;";
46338                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46339             }
46340             if(this.labelAlign == 'top'){
46341                 this.labelStyle = "width:auto;";
46342                 this.elementStyle = "padding-left:0;";
46343             }
46344         }
46345         var stack = this.stack;
46346         var slen = stack.length;
46347         if(slen > 0){
46348             if(!this.fieldTpl){
46349                 var t = new Roo.Template(
46350                     '<div class="x-form-item {5}">',
46351                         '<label for="{0}" style="{2}">{1}{4}</label>',
46352                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46353                         '</div>',
46354                     '</div><div class="x-form-clear-left"></div>'
46355                 );
46356                 t.disableFormats = true;
46357                 t.compile();
46358                 Roo.form.Layout.prototype.fieldTpl = t;
46359             }
46360             for(var i = 0; i < slen; i++) {
46361                 if(stack[i].isFormField){
46362                     this.renderField(stack[i]);
46363                 }else{
46364                     this.renderComponent(stack[i]);
46365                 }
46366             }
46367         }
46368         if(this.clear){
46369             this.el.createChild({cls:'x-form-clear'});
46370         }
46371     },
46372
46373     // private
46374     renderField : function(f){
46375         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46376                f.id, //0
46377                f.fieldLabel, //1
46378                f.labelStyle||this.labelStyle||'', //2
46379                this.elementStyle||'', //3
46380                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46381                f.itemCls||this.itemCls||''  //5
46382        ], true).getPrevSibling());
46383     },
46384
46385     // private
46386     renderComponent : function(c){
46387         c.render(c.isLayout ? this.el : this.el.createChild());    
46388     },
46389     /**
46390      * Adds a object form elements (using the xtype property as the factory method.)
46391      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46392      * @param {Object} config 
46393      */
46394     addxtype : function(o)
46395     {
46396         // create the lement.
46397         o.form = this.form;
46398         var fe = Roo.factory(o, Roo.form);
46399         this.form.allItems.push(fe);
46400         this.stack.push(fe);
46401         
46402         if (fe.isFormField) {
46403             this.form.items.add(fe);
46404         }
46405          
46406         return fe;
46407     }
46408 });
46409
46410 /**
46411  * @class Roo.form.Column
46412  * @extends Roo.form.Layout
46413  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46414  * @constructor
46415  * @param {Object} config Configuration options
46416  */
46417 Roo.form.Column = function(config){
46418     Roo.form.Column.superclass.constructor.call(this, config);
46419 };
46420
46421 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46422     /**
46423      * @cfg {Number/String} width
46424      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46425      */
46426     /**
46427      * @cfg {String/Object} autoCreate
46428      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46429      */
46430
46431     // private
46432     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46433
46434     // private
46435     onRender : function(ct, position){
46436         Roo.form.Column.superclass.onRender.call(this, ct, position);
46437         if(this.width){
46438             this.el.setWidth(this.width);
46439         }
46440     }
46441 });
46442
46443
46444 /**
46445  * @class Roo.form.Row
46446  * @extends Roo.form.Layout
46447  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46448  * @constructor
46449  * @param {Object} config Configuration options
46450  */
46451
46452  
46453 Roo.form.Row = function(config){
46454     Roo.form.Row.superclass.constructor.call(this, config);
46455 };
46456  
46457 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46458       /**
46459      * @cfg {Number/String} width
46460      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46461      */
46462     /**
46463      * @cfg {Number/String} height
46464      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46465      */
46466     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46467     
46468     padWidth : 20,
46469     // private
46470     onRender : function(ct, position){
46471         //console.log('row render');
46472         if(!this.rowTpl){
46473             var t = new Roo.Template(
46474                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46475                     '<label for="{0}" style="{2}">{1}{4}</label>',
46476                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46477                     '</div>',
46478                 '</div>'
46479             );
46480             t.disableFormats = true;
46481             t.compile();
46482             Roo.form.Layout.prototype.rowTpl = t;
46483         }
46484         this.fieldTpl = this.rowTpl;
46485         
46486         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46487         var labelWidth = 100;
46488         
46489         if ((this.labelAlign != 'top')) {
46490             if (typeof this.labelWidth == 'number') {
46491                 labelWidth = this.labelWidth
46492             }
46493             this.padWidth =  20 + labelWidth;
46494             
46495         }
46496         
46497         Roo.form.Column.superclass.onRender.call(this, ct, position);
46498         if(this.width){
46499             this.el.setWidth(this.width);
46500         }
46501         if(this.height){
46502             this.el.setHeight(this.height);
46503         }
46504     },
46505     
46506     // private
46507     renderField : function(f){
46508         f.fieldEl = this.fieldTpl.append(this.el, [
46509                f.id, f.fieldLabel,
46510                f.labelStyle||this.labelStyle||'',
46511                this.elementStyle||'',
46512                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46513                f.itemCls||this.itemCls||'',
46514                f.width ? f.width + this.padWidth : 160 + this.padWidth
46515        ],true);
46516     }
46517 });
46518  
46519
46520 /**
46521  * @class Roo.form.FieldSet
46522  * @extends Roo.form.Layout
46523  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46524  * @constructor
46525  * @param {Object} config Configuration options
46526  */
46527 Roo.form.FieldSet = function(config){
46528     Roo.form.FieldSet.superclass.constructor.call(this, config);
46529 };
46530
46531 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46532     /**
46533      * @cfg {String} legend
46534      * The text to display as the legend for the FieldSet (defaults to '')
46535      */
46536     /**
46537      * @cfg {String/Object} autoCreate
46538      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46539      */
46540
46541     // private
46542     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46543
46544     // private
46545     onRender : function(ct, position){
46546         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46547         if(this.legend){
46548             this.setLegend(this.legend);
46549         }
46550     },
46551
46552     // private
46553     setLegend : function(text){
46554         if(this.rendered){
46555             this.el.child('legend').update(text);
46556         }
46557     }
46558 });/*
46559  * Based on:
46560  * Ext JS Library 1.1.1
46561  * Copyright(c) 2006-2007, Ext JS, LLC.
46562  *
46563  * Originally Released Under LGPL - original licence link has changed is not relivant.
46564  *
46565  * Fork - LGPL
46566  * <script type="text/javascript">
46567  */
46568 /**
46569  * @class Roo.form.VTypes
46570  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46571  * @singleton
46572  */
46573 Roo.form.VTypes = function(){
46574     // closure these in so they are only created once.
46575     var alpha = /^[a-zA-Z_]+$/;
46576     var alphanum = /^[a-zA-Z0-9_]+$/;
46577     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46578     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46579
46580     // All these messages and functions are configurable
46581     return {
46582         /**
46583          * The function used to validate email addresses
46584          * @param {String} value The email address
46585          */
46586         'email' : function(v){
46587             return email.test(v);
46588         },
46589         /**
46590          * The error text to display when the email validation function returns false
46591          * @type String
46592          */
46593         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46594         /**
46595          * The keystroke filter mask to be applied on email input
46596          * @type RegExp
46597          */
46598         'emailMask' : /[a-z0-9_\.\-@]/i,
46599
46600         /**
46601          * The function used to validate URLs
46602          * @param {String} value The URL
46603          */
46604         'url' : function(v){
46605             return url.test(v);
46606         },
46607         /**
46608          * The error text to display when the url validation function returns false
46609          * @type String
46610          */
46611         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46612         
46613         /**
46614          * The function used to validate alpha values
46615          * @param {String} value The value
46616          */
46617         'alpha' : function(v){
46618             return alpha.test(v);
46619         },
46620         /**
46621          * The error text to display when the alpha validation function returns false
46622          * @type String
46623          */
46624         'alphaText' : 'This field should only contain letters and _',
46625         /**
46626          * The keystroke filter mask to be applied on alpha input
46627          * @type RegExp
46628          */
46629         'alphaMask' : /[a-z_]/i,
46630
46631         /**
46632          * The function used to validate alphanumeric values
46633          * @param {String} value The value
46634          */
46635         'alphanum' : function(v){
46636             return alphanum.test(v);
46637         },
46638         /**
46639          * The error text to display when the alphanumeric validation function returns false
46640          * @type String
46641          */
46642         'alphanumText' : 'This field should only contain letters, numbers and _',
46643         /**
46644          * The keystroke filter mask to be applied on alphanumeric input
46645          * @type RegExp
46646          */
46647         'alphanumMask' : /[a-z0-9_]/i
46648     };
46649 }();//<script type="text/javascript">
46650
46651 /**
46652  * @class Roo.form.FCKeditor
46653  * @extends Roo.form.TextArea
46654  * Wrapper around the FCKEditor http://www.fckeditor.net
46655  * @constructor
46656  * Creates a new FCKeditor
46657  * @param {Object} config Configuration options
46658  */
46659 Roo.form.FCKeditor = function(config){
46660     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46661     this.addEvents({
46662          /**
46663          * @event editorinit
46664          * Fired when the editor is initialized - you can add extra handlers here..
46665          * @param {FCKeditor} this
46666          * @param {Object} the FCK object.
46667          */
46668         editorinit : true
46669     });
46670     
46671     
46672 };
46673 Roo.form.FCKeditor.editors = { };
46674 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46675 {
46676     //defaultAutoCreate : {
46677     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46678     //},
46679     // private
46680     /**
46681      * @cfg {Object} fck options - see fck manual for details.
46682      */
46683     fckconfig : false,
46684     
46685     /**
46686      * @cfg {Object} fck toolbar set (Basic or Default)
46687      */
46688     toolbarSet : 'Basic',
46689     /**
46690      * @cfg {Object} fck BasePath
46691      */ 
46692     basePath : '/fckeditor/',
46693     
46694     
46695     frame : false,
46696     
46697     value : '',
46698     
46699    
46700     onRender : function(ct, position)
46701     {
46702         if(!this.el){
46703             this.defaultAutoCreate = {
46704                 tag: "textarea",
46705                 style:"width:300px;height:60px;",
46706                 autocomplete: "off"
46707             };
46708         }
46709         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46710         /*
46711         if(this.grow){
46712             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46713             if(this.preventScrollbars){
46714                 this.el.setStyle("overflow", "hidden");
46715             }
46716             this.el.setHeight(this.growMin);
46717         }
46718         */
46719         //console.log('onrender' + this.getId() );
46720         Roo.form.FCKeditor.editors[this.getId()] = this;
46721          
46722
46723         this.replaceTextarea() ;
46724         
46725     },
46726     
46727     getEditor : function() {
46728         return this.fckEditor;
46729     },
46730     /**
46731      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46732      * @param {Mixed} value The value to set
46733      */
46734     
46735     
46736     setValue : function(value)
46737     {
46738         //console.log('setValue: ' + value);
46739         
46740         if(typeof(value) == 'undefined') { // not sure why this is happending...
46741             return;
46742         }
46743         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46744         
46745         //if(!this.el || !this.getEditor()) {
46746         //    this.value = value;
46747             //this.setValue.defer(100,this,[value]);    
46748         //    return;
46749         //} 
46750         
46751         if(!this.getEditor()) {
46752             return;
46753         }
46754         
46755         this.getEditor().SetData(value);
46756         
46757         //
46758
46759     },
46760
46761     /**
46762      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46763      * @return {Mixed} value The field value
46764      */
46765     getValue : function()
46766     {
46767         
46768         if (this.frame && this.frame.dom.style.display == 'none') {
46769             return Roo.form.FCKeditor.superclass.getValue.call(this);
46770         }
46771         
46772         if(!this.el || !this.getEditor()) {
46773            
46774            // this.getValue.defer(100,this); 
46775             return this.value;
46776         }
46777        
46778         
46779         var value=this.getEditor().GetData();
46780         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46781         return Roo.form.FCKeditor.superclass.getValue.call(this);
46782         
46783
46784     },
46785
46786     /**
46787      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46788      * @return {Mixed} value The field value
46789      */
46790     getRawValue : function()
46791     {
46792         if (this.frame && this.frame.dom.style.display == 'none') {
46793             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46794         }
46795         
46796         if(!this.el || !this.getEditor()) {
46797             //this.getRawValue.defer(100,this); 
46798             return this.value;
46799             return;
46800         }
46801         
46802         
46803         
46804         var value=this.getEditor().GetData();
46805         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46806         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46807          
46808     },
46809     
46810     setSize : function(w,h) {
46811         
46812         
46813         
46814         //if (this.frame && this.frame.dom.style.display == 'none') {
46815         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46816         //    return;
46817         //}
46818         //if(!this.el || !this.getEditor()) {
46819         //    this.setSize.defer(100,this, [w,h]); 
46820         //    return;
46821         //}
46822         
46823         
46824         
46825         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46826         
46827         this.frame.dom.setAttribute('width', w);
46828         this.frame.dom.setAttribute('height', h);
46829         this.frame.setSize(w,h);
46830         
46831     },
46832     
46833     toggleSourceEdit : function(value) {
46834         
46835       
46836          
46837         this.el.dom.style.display = value ? '' : 'none';
46838         this.frame.dom.style.display = value ?  'none' : '';
46839         
46840     },
46841     
46842     
46843     focus: function(tag)
46844     {
46845         if (this.frame.dom.style.display == 'none') {
46846             return Roo.form.FCKeditor.superclass.focus.call(this);
46847         }
46848         if(!this.el || !this.getEditor()) {
46849             this.focus.defer(100,this, [tag]); 
46850             return;
46851         }
46852         
46853         
46854         
46855         
46856         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46857         this.getEditor().Focus();
46858         if (tgs.length) {
46859             if (!this.getEditor().Selection.GetSelection()) {
46860                 this.focus.defer(100,this, [tag]); 
46861                 return;
46862             }
46863             
46864             
46865             var r = this.getEditor().EditorDocument.createRange();
46866             r.setStart(tgs[0],0);
46867             r.setEnd(tgs[0],0);
46868             this.getEditor().Selection.GetSelection().removeAllRanges();
46869             this.getEditor().Selection.GetSelection().addRange(r);
46870             this.getEditor().Focus();
46871         }
46872         
46873     },
46874     
46875     
46876     
46877     replaceTextarea : function()
46878     {
46879         if ( document.getElementById( this.getId() + '___Frame' ) )
46880             return ;
46881         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46882         //{
46883             // We must check the elements firstly using the Id and then the name.
46884         var oTextarea = document.getElementById( this.getId() );
46885         
46886         var colElementsByName = document.getElementsByName( this.getId() ) ;
46887          
46888         oTextarea.style.display = 'none' ;
46889
46890         if ( oTextarea.tabIndex ) {            
46891             this.TabIndex = oTextarea.tabIndex ;
46892         }
46893         
46894         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46895         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46896         this.frame = Roo.get(this.getId() + '___Frame')
46897     },
46898     
46899     _getConfigHtml : function()
46900     {
46901         var sConfig = '' ;
46902
46903         for ( var o in this.fckconfig ) {
46904             sConfig += sConfig.length > 0  ? '&amp;' : '';
46905             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46906         }
46907
46908         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46909     },
46910     
46911     
46912     _getIFrameHtml : function()
46913     {
46914         var sFile = 'fckeditor.html' ;
46915         /* no idea what this is about..
46916         try
46917         {
46918             if ( (/fcksource=true/i).test( window.top.location.search ) )
46919                 sFile = 'fckeditor.original.html' ;
46920         }
46921         catch (e) { 
46922         */
46923
46924         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46925         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46926         
46927         
46928         var html = '<iframe id="' + this.getId() +
46929             '___Frame" src="' + sLink +
46930             '" width="' + this.width +
46931             '" height="' + this.height + '"' +
46932             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46933             ' frameborder="0" scrolling="no"></iframe>' ;
46934
46935         return html ;
46936     },
46937     
46938     _insertHtmlBefore : function( html, element )
46939     {
46940         if ( element.insertAdjacentHTML )       {
46941             // IE
46942             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46943         } else { // Gecko
46944             var oRange = document.createRange() ;
46945             oRange.setStartBefore( element ) ;
46946             var oFragment = oRange.createContextualFragment( html );
46947             element.parentNode.insertBefore( oFragment, element ) ;
46948         }
46949     }
46950     
46951     
46952   
46953     
46954     
46955     
46956     
46957
46958 });
46959
46960 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46961
46962 function FCKeditor_OnComplete(editorInstance){
46963     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46964     f.fckEditor = editorInstance;
46965     //console.log("loaded");
46966     f.fireEvent('editorinit', f, editorInstance);
46967
46968   
46969
46970  
46971
46972
46973
46974
46975
46976
46977
46978
46979
46980
46981
46982
46983
46984
46985
46986 //<script type="text/javascript">
46987 /**
46988  * @class Roo.form.GridField
46989  * @extends Roo.form.Field
46990  * Embed a grid (or editable grid into a form)
46991  * STATUS ALPHA
46992  * 
46993  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
46994  * it needs 
46995  * xgrid.store = Roo.data.Store
46996  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
46997  * xgrid.store.reader = Roo.data.JsonReader 
46998  * 
46999  * 
47000  * @constructor
47001  * Creates a new GridField
47002  * @param {Object} config Configuration options
47003  */
47004 Roo.form.GridField = function(config){
47005     Roo.form.GridField.superclass.constructor.call(this, config);
47006      
47007 };
47008
47009 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47010     /**
47011      * @cfg {Number} width  - used to restrict width of grid..
47012      */
47013     width : 100,
47014     /**
47015      * @cfg {Number} height - used to restrict height of grid..
47016      */
47017     height : 50,
47018      /**
47019      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47020          * 
47021          *}
47022      */
47023     xgrid : false, 
47024     /**
47025      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47026      * {tag: "input", type: "checkbox", autocomplete: "off"})
47027      */
47028    // defaultAutoCreate : { tag: 'div' },
47029     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47030     /**
47031      * @cfg {String} addTitle Text to include for adding a title.
47032      */
47033     addTitle : false,
47034     //
47035     onResize : function(){
47036         Roo.form.Field.superclass.onResize.apply(this, arguments);
47037     },
47038
47039     initEvents : function(){
47040         // Roo.form.Checkbox.superclass.initEvents.call(this);
47041         // has no events...
47042        
47043     },
47044
47045
47046     getResizeEl : function(){
47047         return this.wrap;
47048     },
47049
47050     getPositionEl : function(){
47051         return this.wrap;
47052     },
47053
47054     // private
47055     onRender : function(ct, position){
47056         
47057         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47058         var style = this.style;
47059         delete this.style;
47060         
47061         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47062         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47063         this.viewEl = this.wrap.createChild({ tag: 'div' });
47064         if (style) {
47065             this.viewEl.applyStyles(style);
47066         }
47067         if (this.width) {
47068             this.viewEl.setWidth(this.width);
47069         }
47070         if (this.height) {
47071             this.viewEl.setHeight(this.height);
47072         }
47073         //if(this.inputValue !== undefined){
47074         //this.setValue(this.value);
47075         
47076         
47077         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47078         
47079         
47080         this.grid.render();
47081         this.grid.getDataSource().on('remove', this.refreshValue, this);
47082         this.grid.getDataSource().on('update', this.refreshValue, this);
47083         this.grid.on('afteredit', this.refreshValue, this);
47084  
47085     },
47086      
47087     
47088     /**
47089      * Sets the value of the item. 
47090      * @param {String} either an object  or a string..
47091      */
47092     setValue : function(v){
47093         //this.value = v;
47094         v = v || []; // empty set..
47095         // this does not seem smart - it really only affects memoryproxy grids..
47096         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47097             var ds = this.grid.getDataSource();
47098             // assumes a json reader..
47099             var data = {}
47100             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47101             ds.loadData( data);
47102         }
47103         // clear selection so it does not get stale.
47104         if (this.grid.sm) { 
47105             this.grid.sm.clearSelections();
47106         }
47107         
47108         Roo.form.GridField.superclass.setValue.call(this, v);
47109         this.refreshValue();
47110         // should load data in the grid really....
47111     },
47112     
47113     // private
47114     refreshValue: function() {
47115          var val = [];
47116         this.grid.getDataSource().each(function(r) {
47117             val.push(r.data);
47118         });
47119         this.el.dom.value = Roo.encode(val);
47120     }
47121     
47122      
47123     
47124     
47125 });/*
47126  * Based on:
47127  * Ext JS Library 1.1.1
47128  * Copyright(c) 2006-2007, Ext JS, LLC.
47129  *
47130  * Originally Released Under LGPL - original licence link has changed is not relivant.
47131  *
47132  * Fork - LGPL
47133  * <script type="text/javascript">
47134  */
47135 /**
47136  * @class Roo.form.DisplayField
47137  * @extends Roo.form.Field
47138  * A generic Field to display non-editable data.
47139  * @constructor
47140  * Creates a new Display Field item.
47141  * @param {Object} config Configuration options
47142  */
47143 Roo.form.DisplayField = function(config){
47144     Roo.form.DisplayField.superclass.constructor.call(this, config);
47145     
47146 };
47147
47148 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47149     inputType:      'hidden',
47150     allowBlank:     true,
47151     readOnly:         true,
47152     
47153  
47154     /**
47155      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47156      */
47157     focusClass : undefined,
47158     /**
47159      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47160      */
47161     fieldClass: 'x-form-field',
47162     
47163      /**
47164      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47165      */
47166     valueRenderer: undefined,
47167     
47168     width: 100,
47169     /**
47170      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47171      * {tag: "input", type: "checkbox", autocomplete: "off"})
47172      */
47173      
47174  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47175
47176     onResize : function(){
47177         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47178         
47179     },
47180
47181     initEvents : function(){
47182         // Roo.form.Checkbox.superclass.initEvents.call(this);
47183         // has no events...
47184        
47185     },
47186
47187
47188     getResizeEl : function(){
47189         return this.wrap;
47190     },
47191
47192     getPositionEl : function(){
47193         return this.wrap;
47194     },
47195
47196     // private
47197     onRender : function(ct, position){
47198         
47199         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47200         //if(this.inputValue !== undefined){
47201         this.wrap = this.el.wrap();
47202         
47203         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47204         
47205         if (this.bodyStyle) {
47206             this.viewEl.applyStyles(this.bodyStyle);
47207         }
47208         //this.viewEl.setStyle('padding', '2px');
47209         
47210         this.setValue(this.value);
47211         
47212     },
47213 /*
47214     // private
47215     initValue : Roo.emptyFn,
47216
47217   */
47218
47219         // private
47220     onClick : function(){
47221         
47222     },
47223
47224     /**
47225      * Sets the checked state of the checkbox.
47226      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47227      */
47228     setValue : function(v){
47229         this.value = v;
47230         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47231         // this might be called before we have a dom element..
47232         if (!this.viewEl) {
47233             return;
47234         }
47235         this.viewEl.dom.innerHTML = html;
47236         Roo.form.DisplayField.superclass.setValue.call(this, v);
47237
47238     }
47239 });/*
47240  * 
47241  * Licence- LGPL
47242  * 
47243  */
47244
47245 /**
47246  * @class Roo.form.DayPicker
47247  * @extends Roo.form.Field
47248  * A Day picker show [M] [T] [W] ....
47249  * @constructor
47250  * Creates a new Day Picker
47251  * @param {Object} config Configuration options
47252  */
47253 Roo.form.DayPicker= function(config){
47254     Roo.form.DayPicker.superclass.constructor.call(this, config);
47255      
47256 };
47257
47258 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47259     /**
47260      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47261      */
47262     focusClass : undefined,
47263     /**
47264      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47265      */
47266     fieldClass: "x-form-field",
47267    
47268     /**
47269      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47270      * {tag: "input", type: "checkbox", autocomplete: "off"})
47271      */
47272     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
47273     
47274    
47275     actionMode : 'viewEl', 
47276     //
47277     // private
47278  
47279     inputType : 'hidden',
47280     
47281      
47282     inputElement: false, // real input element?
47283     basedOn: false, // ????
47284     
47285     isFormField: true, // not sure where this is needed!!!!
47286
47287     onResize : function(){
47288         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47289         if(!this.boxLabel){
47290             this.el.alignTo(this.wrap, 'c-c');
47291         }
47292     },
47293
47294     initEvents : function(){
47295         Roo.form.Checkbox.superclass.initEvents.call(this);
47296         this.el.on("click", this.onClick,  this);
47297         this.el.on("change", this.onClick,  this);
47298     },
47299
47300
47301     getResizeEl : function(){
47302         return this.wrap;
47303     },
47304
47305     getPositionEl : function(){
47306         return this.wrap;
47307     },
47308
47309     
47310     // private
47311     onRender : function(ct, position){
47312         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47313        
47314         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47315         
47316         var r1 = '<table><tr>';
47317         var r2 = '<tr class="x-form-daypick-icons">';
47318         for (var i=0; i < 7; i++) {
47319             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47320             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47321         }
47322         
47323         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47324         viewEl.select('img').on('click', this.onClick, this);
47325         this.viewEl = viewEl;   
47326         
47327         
47328         // this will not work on Chrome!!!
47329         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47330         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47331         
47332         
47333           
47334
47335     },
47336
47337     // private
47338     initValue : Roo.emptyFn,
47339
47340     /**
47341      * Returns the checked state of the checkbox.
47342      * @return {Boolean} True if checked, else false
47343      */
47344     getValue : function(){
47345         return this.el.dom.value;
47346         
47347     },
47348
47349         // private
47350     onClick : function(e){ 
47351         //this.setChecked(!this.checked);
47352         Roo.get(e.target).toggleClass('x-menu-item-checked');
47353         this.refreshValue();
47354         //if(this.el.dom.checked != this.checked){
47355         //    this.setValue(this.el.dom.checked);
47356        // }
47357     },
47358     
47359     // private
47360     refreshValue : function()
47361     {
47362         var val = '';
47363         this.viewEl.select('img',true).each(function(e,i,n)  {
47364             val += e.is(".x-menu-item-checked") ? String(n) : '';
47365         });
47366         this.setValue(val, true);
47367     },
47368
47369     /**
47370      * Sets the checked state of the checkbox.
47371      * On is always based on a string comparison between inputValue and the param.
47372      * @param {Boolean/String} value - the value to set 
47373      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47374      */
47375     setValue : function(v,suppressEvent){
47376         if (!this.el.dom) {
47377             return;
47378         }
47379         var old = this.el.dom.value ;
47380         this.el.dom.value = v;
47381         if (suppressEvent) {
47382             return ;
47383         }
47384          
47385         // update display..
47386         this.viewEl.select('img',true).each(function(e,i,n)  {
47387             
47388             var on = e.is(".x-menu-item-checked");
47389             var newv = v.indexOf(String(n)) > -1;
47390             if (on != newv) {
47391                 e.toggleClass('x-menu-item-checked');
47392             }
47393             
47394         });
47395         
47396         
47397         this.fireEvent('change', this, v, old);
47398         
47399         
47400     },
47401    
47402     // handle setting of hidden value by some other method!!?!?
47403     setFromHidden: function()
47404     {
47405         if(!this.el){
47406             return;
47407         }
47408         //console.log("SET FROM HIDDEN");
47409         //alert('setFrom hidden');
47410         this.setValue(this.el.dom.value);
47411     },
47412     
47413     onDestroy : function()
47414     {
47415         if(this.viewEl){
47416             Roo.get(this.viewEl).remove();
47417         }
47418          
47419         Roo.form.DayPicker.superclass.onDestroy.call(this);
47420     }
47421
47422 });/*
47423  * RooJS Library 1.1.1
47424  * Copyright(c) 2008-2011  Alan Knowles
47425  *
47426  * License - LGPL
47427  */
47428  
47429
47430 /**
47431  * @class Roo.form.ComboCheck
47432  * @extends Roo.form.ComboBox
47433  * A combobox for multiple select items.
47434  *
47435  * FIXME - could do with a reset button..
47436  * 
47437  * @constructor
47438  * Create a new ComboCheck
47439  * @param {Object} config Configuration options
47440  */
47441 Roo.form.ComboCheck = function(config){
47442     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47443     // should verify some data...
47444     // like
47445     // hiddenName = required..
47446     // displayField = required
47447     // valudField == required
47448     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47449     var _t = this;
47450     Roo.each(req, function(e) {
47451         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47452             throw "Roo.form.ComboCheck : missing value for: " + e;
47453         }
47454     });
47455     
47456     
47457 };
47458
47459 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47460      
47461      
47462     editable : false,
47463      
47464     selectedClass: 'x-menu-item-checked', 
47465     
47466     // private
47467     onRender : function(ct, position){
47468         var _t = this;
47469         
47470         
47471         
47472         if(!this.tpl){
47473             var cls = 'x-combo-list';
47474
47475             
47476             this.tpl =  new Roo.Template({
47477                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47478                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47479                    '<span>{' + this.displayField + '}</span>' +
47480                     '</div>' 
47481                 
47482             });
47483         }
47484  
47485         
47486         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47487         this.view.singleSelect = false;
47488         this.view.multiSelect = true;
47489         this.view.toggleSelect = true;
47490         this.pageTb.add(new Roo.Toolbar.Fill(), {
47491             
47492             text: 'Done',
47493             handler: function()
47494             {
47495                 _t.collapse();
47496             }
47497         });
47498     },
47499     
47500     onViewOver : function(e, t){
47501         // do nothing...
47502         return;
47503         
47504     },
47505     
47506     onViewClick : function(doFocus,index){
47507         return;
47508         
47509     },
47510     select: function () {
47511         //Roo.log("SELECT CALLED");
47512     },
47513      
47514     selectByValue : function(xv, scrollIntoView){
47515         var ar = this.getValueArray();
47516         var sels = [];
47517         
47518         Roo.each(ar, function(v) {
47519             if(v === undefined || v === null){
47520                 return;
47521             }
47522             var r = this.findRecord(this.valueField, v);
47523             if(r){
47524                 sels.push(this.store.indexOf(r))
47525                 
47526             }
47527         },this);
47528         this.view.select(sels);
47529         return false;
47530     },
47531     
47532     
47533     
47534     onSelect : function(record, index){
47535        // Roo.log("onselect Called");
47536        // this is only called by the clear button now..
47537         this.view.clearSelections();
47538         this.setValue('[]');
47539         if (this.value != this.valueBefore) {
47540             this.fireEvent('change', this, this.value, this.valueBefore);
47541             this.valueBefore = this.value;
47542         }
47543     },
47544     getValueArray : function()
47545     {
47546         var ar = [] ;
47547         
47548         try {
47549             //Roo.log(this.value);
47550             if (typeof(this.value) == 'undefined') {
47551                 return [];
47552             }
47553             var ar = Roo.decode(this.value);
47554             return  ar instanceof Array ? ar : []; //?? valid?
47555             
47556         } catch(e) {
47557             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47558             return [];
47559         }
47560          
47561     },
47562     expand : function ()
47563     {
47564         
47565         Roo.form.ComboCheck.superclass.expand.call(this);
47566         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47567         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47568         
47569
47570     },
47571     
47572     collapse : function(){
47573         Roo.form.ComboCheck.superclass.collapse.call(this);
47574         var sl = this.view.getSelectedIndexes();
47575         var st = this.store;
47576         var nv = [];
47577         var tv = [];
47578         var r;
47579         Roo.each(sl, function(i) {
47580             r = st.getAt(i);
47581             nv.push(r.get(this.valueField));
47582         },this);
47583         this.setValue(Roo.encode(nv));
47584         if (this.value != this.valueBefore) {
47585
47586             this.fireEvent('change', this, this.value, this.valueBefore);
47587             this.valueBefore = this.value;
47588         }
47589         
47590     },
47591     
47592     setValue : function(v){
47593         // Roo.log(v);
47594         this.value = v;
47595         
47596         var vals = this.getValueArray();
47597         var tv = [];
47598         Roo.each(vals, function(k) {
47599             var r = this.findRecord(this.valueField, k);
47600             if(r){
47601                 tv.push(r.data[this.displayField]);
47602             }else if(this.valueNotFoundText !== undefined){
47603                 tv.push( this.valueNotFoundText );
47604             }
47605         },this);
47606        // Roo.log(tv);
47607         
47608         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47609         this.hiddenField.value = v;
47610         this.value = v;
47611     }
47612     
47613 });/*
47614  * Based on:
47615  * Ext JS Library 1.1.1
47616  * Copyright(c) 2006-2007, Ext JS, LLC.
47617  *
47618  * Originally Released Under LGPL - original licence link has changed is not relivant.
47619  *
47620  * Fork - LGPL
47621  * <script type="text/javascript">
47622  */
47623  
47624 /**
47625  * @class Roo.form.Signature
47626  * @extends Roo.form.Field
47627  * Signature field.  
47628  * @constructor
47629  * 
47630  * @param {Object} config Configuration options
47631  */
47632
47633 Roo.form.Signature = function(config){
47634     Roo.form.Signature.superclass.constructor.call(this, config);
47635     
47636     this.addEvents({// not in used??
47637          /**
47638          * @event confirm
47639          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47640              * @param {Roo.form.Signature} combo This combo box
47641              */
47642         'confirm' : true,
47643         /**
47644          * @event reset
47645          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47646              * @param {Roo.form.ComboBox} combo This combo box
47647              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47648              */
47649         'reset' : true
47650     });
47651 };
47652
47653 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47654     /**
47655      * @cfg {Object} labels Label to use when rendering a form.
47656      * defaults to 
47657      * labels : { 
47658      *      clear : "Clear",
47659      *      confirm : "Confirm"
47660      *  }
47661      */
47662     labels : { 
47663         clear : "Clear",
47664         confirm : "Confirm"
47665     },
47666     /**
47667      * @cfg {Number} width The signature panel width (defaults to 300)
47668      */
47669     width: 300,
47670     /**
47671      * @cfg {Number} height The signature panel height (defaults to 100)
47672      */
47673     height : 100,
47674     /**
47675      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47676      */
47677     allowBlank : false,
47678     
47679     //private
47680     // {Object} signPanel The signature SVG panel element (defaults to {})
47681     signPanel : {},
47682     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47683     isMouseDown : false,
47684     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47685     isConfirmed : false,
47686     // {String} signatureTmp SVG mapping string (defaults to empty string)
47687     signatureTmp : '',
47688     
47689     
47690     defaultAutoCreate : { // modified by initCompnoent..
47691         tag: "input",
47692         type:"hidden"
47693     },
47694
47695     // private
47696     onRender : function(ct, position){
47697         
47698         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47699         
47700         this.wrap = this.el.wrap({
47701             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47702         });
47703         
47704         this.createToolbar(this);
47705         this.signPanel = this.wrap.createChild({
47706                 tag: 'div',
47707                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47708             }, this.el
47709         );
47710             
47711         this.svgID = Roo.id();
47712         this.svgEl = this.signPanel.createChild({
47713               xmlns : 'http://www.w3.org/2000/svg',
47714               tag : 'svg',
47715               id : this.svgID + "-svg",
47716               width: this.width,
47717               height: this.height,
47718               viewBox: '0 0 '+this.width+' '+this.height,
47719               cn : [
47720                 {
47721                     tag: "rect",
47722                     id: this.svgID + "-svg-r",
47723                     width: this.width,
47724                     height: this.height,
47725                     fill: "#ffa"
47726                 },
47727                 {
47728                     tag: "line",
47729                     id: this.svgID + "-svg-l",
47730                     x1: "0", // start
47731                     y1: (this.height*0.8), // start set the line in 80% of height
47732                     x2: this.width, // end
47733                     y2: (this.height*0.8), // end set the line in 80% of height
47734                     'stroke': "#666",
47735                     'stroke-width': "1",
47736                     'stroke-dasharray': "3",
47737                     'shape-rendering': "crispEdges",
47738                     'pointer-events': "none"
47739                 },
47740                 {
47741                     tag: "path",
47742                     id: this.svgID + "-svg-p",
47743                     'stroke': "navy",
47744                     'stroke-width': "3",
47745                     'fill': "none",
47746                     'pointer-events': 'none'
47747                 }
47748               ]
47749         });
47750         this.createSVG();
47751         this.svgBox = this.svgEl.dom.getScreenCTM();
47752     },
47753     createSVG : function(){ 
47754         var svg = this.signPanel;
47755         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47756         var t = this;
47757
47758         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47759         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47760         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47761         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47762         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47763         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47764         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47765         
47766     },
47767     isTouchEvent : function(e){
47768         return e.type.match(/^touch/);
47769     },
47770     getCoords : function (e) {
47771         var pt    = this.svgEl.dom.createSVGPoint();
47772         pt.x = e.clientX; 
47773         pt.y = e.clientY;
47774         if (this.isTouchEvent(e)) {
47775             pt.x =  e.targetTouches[0].clientX 
47776             pt.y = e.targetTouches[0].clientY;
47777         }
47778         var a = this.svgEl.dom.getScreenCTM();
47779         var b = a.inverse();
47780         var mx = pt.matrixTransform(b);
47781         return mx.x + ',' + mx.y;
47782     },
47783     //mouse event headler 
47784     down : function (e) {
47785         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47786         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47787         
47788         this.isMouseDown = true;
47789         
47790         e.preventDefault();
47791     },
47792     move : function (e) {
47793         if (this.isMouseDown) {
47794             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47795             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47796         }
47797         
47798         e.preventDefault();
47799     },
47800     up : function (e) {
47801         this.isMouseDown = false;
47802         var sp = this.signatureTmp.split(' ');
47803         
47804         if(sp.length > 1){
47805             if(!sp[sp.length-2].match(/^L/)){
47806                 sp.pop();
47807                 sp.pop();
47808                 sp.push("");
47809                 this.signatureTmp = sp.join(" ");
47810             }
47811         }
47812         if(this.getValue() != this.signatureTmp){
47813             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47814             this.isConfirmed = false;
47815         }
47816         e.preventDefault();
47817     },
47818     
47819     /**
47820      * Protected method that will not generally be called directly. It
47821      * is called when the editor creates its toolbar. Override this method if you need to
47822      * add custom toolbar buttons.
47823      * @param {HtmlEditor} editor
47824      */
47825     createToolbar : function(editor){
47826          function btn(id, toggle, handler){
47827             var xid = fid + '-'+ id ;
47828             return {
47829                 id : xid,
47830                 cmd : id,
47831                 cls : 'x-btn-icon x-edit-'+id,
47832                 enableToggle:toggle !== false,
47833                 scope: editor, // was editor...
47834                 handler:handler||editor.relayBtnCmd,
47835                 clickEvent:'mousedown',
47836                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47837                 tabIndex:-1
47838             };
47839         }
47840         
47841         
47842         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47843         this.tb = tb;
47844         this.tb.add(
47845            {
47846                 cls : ' x-signature-btn x-signature-'+id,
47847                 scope: editor, // was editor...
47848                 handler: this.reset,
47849                 clickEvent:'mousedown',
47850                 text: this.labels.clear
47851             },
47852             {
47853                  xtype : 'Fill',
47854                  xns: Roo.Toolbar
47855             }, 
47856             {
47857                 cls : '  x-signature-btn x-signature-'+id,
47858                 scope: editor, // was editor...
47859                 handler: this.confirmHandler,
47860                 clickEvent:'mousedown',
47861                 text: this.labels.confirm
47862             }
47863         );
47864     
47865     },
47866     //public
47867     /**
47868      * when user is clicked confirm then show this image.....
47869      * 
47870      * @return {String} Image Data URI
47871      */
47872     getImageDataURI : function(){
47873         var svg = this.svgEl.dom.parentNode.innerHTML;
47874         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47875         return src; 
47876     },
47877     /**
47878      * 
47879      * @return {Boolean} this.isConfirmed
47880      */
47881     getConfirmed : function(){
47882         return this.isConfirmed;
47883     },
47884     /**
47885      * 
47886      * @return {Number} this.width
47887      */
47888     getWidth : function(){
47889         return this.width;
47890     },
47891     /**
47892      * 
47893      * @return {Number} this.height
47894      */
47895     getHeight : function(){
47896         return this.height;
47897     },
47898     // private
47899     getSignature : function(){
47900         return this.signatureTmp;
47901     },
47902     // private
47903     reset : function(){
47904         this.signatureTmp = '';
47905         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47906         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47907         this.isConfirmed = false;
47908         Roo.form.Signature.superclass.reset.call(this);
47909     },
47910     setSignature : function(s){
47911         this.signatureTmp = s;
47912         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47913         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47914         this.setValue(s);
47915         this.isConfirmed = false;
47916         Roo.form.Signature.superclass.reset.call(this);
47917     }, 
47918     test : function(){
47919 //        Roo.log(this.signPanel.dom.contentWindow.up())
47920     },
47921     //private
47922     setConfirmed : function(){
47923         
47924         
47925         
47926 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47927     },
47928     // private
47929     confirmHandler : function(){
47930         if(!this.getSignature()){
47931             return;
47932         }
47933         
47934         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47935         this.setValue(this.getSignature());
47936         this.isConfirmed = true;
47937         
47938         this.fireEvent('confirm', this);
47939     },
47940     // private
47941     // Subclasses should provide the validation implementation by overriding this
47942     validateValue : function(value){
47943         if(this.allowBlank){
47944             return true;
47945         }
47946         
47947         if(this.isConfirmed){
47948             return true;
47949         }
47950         return false;
47951     }
47952 });/*
47953  * Based on:
47954  * Ext JS Library 1.1.1
47955  * Copyright(c) 2006-2007, Ext JS, LLC.
47956  *
47957  * Originally Released Under LGPL - original licence link has changed is not relivant.
47958  *
47959  * Fork - LGPL
47960  * <script type="text/javascript">
47961  */
47962  
47963
47964 /**
47965  * @class Roo.form.ComboBox
47966  * @extends Roo.form.TriggerField
47967  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47968  * @constructor
47969  * Create a new ComboBox.
47970  * @param {Object} config Configuration options
47971  */
47972 Roo.form.Select = function(config){
47973     Roo.form.Select.superclass.constructor.call(this, config);
47974      
47975 };
47976
47977 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47978     /**
47979      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47980      */
47981     /**
47982      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47983      * rendering into an Roo.Editor, defaults to false)
47984      */
47985     /**
47986      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47987      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47988      */
47989     /**
47990      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
47991      */
47992     /**
47993      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
47994      * the dropdown list (defaults to undefined, with no header element)
47995      */
47996
47997      /**
47998      * @cfg {String/Roo.Template} tpl The template to use to render the output
47999      */
48000      
48001     // private
48002     defaultAutoCreate : {tag: "select"  },
48003     /**
48004      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48005      */
48006     listWidth: undefined,
48007     /**
48008      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48009      * mode = 'remote' or 'text' if mode = 'local')
48010      */
48011     displayField: undefined,
48012     /**
48013      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48014      * mode = 'remote' or 'value' if mode = 'local'). 
48015      * Note: use of a valueField requires the user make a selection
48016      * in order for a value to be mapped.
48017      */
48018     valueField: undefined,
48019     
48020     
48021     /**
48022      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48023      * field's data value (defaults to the underlying DOM element's name)
48024      */
48025     hiddenName: undefined,
48026     /**
48027      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48028      */
48029     listClass: '',
48030     /**
48031      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48032      */
48033     selectedClass: 'x-combo-selected',
48034     /**
48035      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48036      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48037      * which displays a downward arrow icon).
48038      */
48039     triggerClass : 'x-form-arrow-trigger',
48040     /**
48041      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48042      */
48043     shadow:'sides',
48044     /**
48045      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48046      * anchor positions (defaults to 'tl-bl')
48047      */
48048     listAlign: 'tl-bl?',
48049     /**
48050      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48051      */
48052     maxHeight: 300,
48053     /**
48054      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48055      * query specified by the allQuery config option (defaults to 'query')
48056      */
48057     triggerAction: 'query',
48058     /**
48059      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48060      * (defaults to 4, does not apply if editable = false)
48061      */
48062     minChars : 4,
48063     /**
48064      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48065      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48066      */
48067     typeAhead: false,
48068     /**
48069      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48070      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48071      */
48072     queryDelay: 500,
48073     /**
48074      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48075      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48076      */
48077     pageSize: 0,
48078     /**
48079      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48080      * when editable = true (defaults to false)
48081      */
48082     selectOnFocus:false,
48083     /**
48084      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48085      */
48086     queryParam: 'query',
48087     /**
48088      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48089      * when mode = 'remote' (defaults to 'Loading...')
48090      */
48091     loadingText: 'Loading...',
48092     /**
48093      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48094      */
48095     resizable: false,
48096     /**
48097      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48098      */
48099     handleHeight : 8,
48100     /**
48101      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48102      * traditional select (defaults to true)
48103      */
48104     editable: true,
48105     /**
48106      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48107      */
48108     allQuery: '',
48109     /**
48110      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48111      */
48112     mode: 'remote',
48113     /**
48114      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48115      * listWidth has a higher value)
48116      */
48117     minListWidth : 70,
48118     /**
48119      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48120      * allow the user to set arbitrary text into the field (defaults to false)
48121      */
48122     forceSelection:false,
48123     /**
48124      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48125      * if typeAhead = true (defaults to 250)
48126      */
48127     typeAheadDelay : 250,
48128     /**
48129      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48130      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48131      */
48132     valueNotFoundText : undefined,
48133     
48134     /**
48135      * @cfg {String} defaultValue The value displayed after loading the store.
48136      */
48137     defaultValue: '',
48138     
48139     /**
48140      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48141      */
48142     blockFocus : false,
48143     
48144     /**
48145      * @cfg {Boolean} disableClear Disable showing of clear button.
48146      */
48147     disableClear : false,
48148     /**
48149      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48150      */
48151     alwaysQuery : false,
48152     
48153     //private
48154     addicon : false,
48155     editicon: false,
48156     
48157     // element that contains real text value.. (when hidden is used..)
48158      
48159     // private
48160     onRender : function(ct, position){
48161         Roo.form.Field.prototype.onRender.call(this, ct, position);
48162         
48163         if(this.store){
48164             this.store.on('beforeload', this.onBeforeLoad, this);
48165             this.store.on('load', this.onLoad, this);
48166             this.store.on('loadexception', this.onLoadException, this);
48167             this.store.load({});
48168         }
48169         
48170         
48171         
48172     },
48173
48174     // private
48175     initEvents : function(){
48176         //Roo.form.ComboBox.superclass.initEvents.call(this);
48177  
48178     },
48179
48180     onDestroy : function(){
48181        
48182         if(this.store){
48183             this.store.un('beforeload', this.onBeforeLoad, this);
48184             this.store.un('load', this.onLoad, this);
48185             this.store.un('loadexception', this.onLoadException, this);
48186         }
48187         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48188     },
48189
48190     // private
48191     fireKey : function(e){
48192         if(e.isNavKeyPress() && !this.list.isVisible()){
48193             this.fireEvent("specialkey", this, e);
48194         }
48195     },
48196
48197     // private
48198     onResize: function(w, h){
48199         
48200         return; 
48201     
48202         
48203     },
48204
48205     /**
48206      * Allow or prevent the user from directly editing the field text.  If false is passed,
48207      * the user will only be able to select from the items defined in the dropdown list.  This method
48208      * is the runtime equivalent of setting the 'editable' config option at config time.
48209      * @param {Boolean} value True to allow the user to directly edit the field text
48210      */
48211     setEditable : function(value){
48212          
48213     },
48214
48215     // private
48216     onBeforeLoad : function(){
48217         
48218         Roo.log("Select before load");
48219         return;
48220     
48221         this.innerList.update(this.loadingText ?
48222                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48223         //this.restrictHeight();
48224         this.selectedIndex = -1;
48225     },
48226
48227     // private
48228     onLoad : function(){
48229
48230     
48231         var dom = this.el.dom;
48232         dom.innerHTML = '';
48233          var od = dom.ownerDocument;
48234          
48235         if (this.emptyText) {
48236             var op = od.createElement('option');
48237             op.setAttribute('value', '');
48238             op.innerHTML = String.format('{0}', this.emptyText);
48239             dom.appendChild(op);
48240         }
48241         if(this.store.getCount() > 0){
48242            
48243             var vf = this.valueField;
48244             var df = this.displayField;
48245             this.store.data.each(function(r) {
48246                 // which colmsn to use... testing - cdoe / title..
48247                 var op = od.createElement('option');
48248                 op.setAttribute('value', r.data[vf]);
48249                 op.innerHTML = String.format('{0}', r.data[df]);
48250                 dom.appendChild(op);
48251             });
48252             if (typeof(this.defaultValue != 'undefined')) {
48253                 this.setValue(this.defaultValue);
48254             }
48255             
48256              
48257         }else{
48258             //this.onEmptyResults();
48259         }
48260         //this.el.focus();
48261     },
48262     // private
48263     onLoadException : function()
48264     {
48265         dom.innerHTML = '';
48266             
48267         Roo.log("Select on load exception");
48268         return;
48269     
48270         this.collapse();
48271         Roo.log(this.store.reader.jsonData);
48272         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48273             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48274         }
48275         
48276         
48277     },
48278     // private
48279     onTypeAhead : function(){
48280          
48281     },
48282
48283     // private
48284     onSelect : function(record, index){
48285         Roo.log('on select?');
48286         return;
48287         if(this.fireEvent('beforeselect', this, record, index) !== false){
48288             this.setFromData(index > -1 ? record.data : false);
48289             this.collapse();
48290             this.fireEvent('select', this, record, index);
48291         }
48292     },
48293
48294     /**
48295      * Returns the currently selected field value or empty string if no value is set.
48296      * @return {String} value The selected value
48297      */
48298     getValue : function(){
48299         var dom = this.el.dom;
48300         this.value = dom.options[dom.selectedIndex].value;
48301         return this.value;
48302         
48303     },
48304
48305     /**
48306      * Clears any text/value currently set in the field
48307      */
48308     clearValue : function(){
48309         this.value = '';
48310         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48311         
48312     },
48313
48314     /**
48315      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48316      * will be displayed in the field.  If the value does not match the data value of an existing item,
48317      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48318      * Otherwise the field will be blank (although the value will still be set).
48319      * @param {String} value The value to match
48320      */
48321     setValue : function(v){
48322         var d = this.el.dom;
48323         for (var i =0; i < d.options.length;i++) {
48324             if (v == d.options[i].value) {
48325                 d.selectedIndex = i;
48326                 this.value = v;
48327                 return;
48328             }
48329         }
48330         this.clearValue();
48331     },
48332     /**
48333      * @property {Object} the last set data for the element
48334      */
48335     
48336     lastData : false,
48337     /**
48338      * Sets the value of the field based on a object which is related to the record format for the store.
48339      * @param {Object} value the value to set as. or false on reset?
48340      */
48341     setFromData : function(o){
48342         Roo.log('setfrom data?');
48343          
48344         
48345         
48346     },
48347     // private
48348     reset : function(){
48349         this.clearValue();
48350     },
48351     // private
48352     findRecord : function(prop, value){
48353         
48354         return false;
48355     
48356         var record;
48357         if(this.store.getCount() > 0){
48358             this.store.each(function(r){
48359                 if(r.data[prop] == value){
48360                     record = r;
48361                     return false;
48362                 }
48363                 return true;
48364             });
48365         }
48366         return record;
48367     },
48368     
48369     getName: function()
48370     {
48371         // returns hidden if it's set..
48372         if (!this.rendered) {return ''};
48373         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48374         
48375     },
48376      
48377
48378     
48379
48380     // private
48381     onEmptyResults : function(){
48382         Roo.log('empty results');
48383         //this.collapse();
48384     },
48385
48386     /**
48387      * Returns true if the dropdown list is expanded, else false.
48388      */
48389     isExpanded : function(){
48390         return false;
48391     },
48392
48393     /**
48394      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48395      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48396      * @param {String} value The data value of the item to select
48397      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48398      * selected item if it is not currently in view (defaults to true)
48399      * @return {Boolean} True if the value matched an item in the list, else false
48400      */
48401     selectByValue : function(v, scrollIntoView){
48402         Roo.log('select By Value');
48403         return false;
48404     
48405         if(v !== undefined && v !== null){
48406             var r = this.findRecord(this.valueField || this.displayField, v);
48407             if(r){
48408                 this.select(this.store.indexOf(r), scrollIntoView);
48409                 return true;
48410             }
48411         }
48412         return false;
48413     },
48414
48415     /**
48416      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48417      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48418      * @param {Number} index The zero-based index of the list item to select
48419      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48420      * selected item if it is not currently in view (defaults to true)
48421      */
48422     select : function(index, scrollIntoView){
48423         Roo.log('select ');
48424         return  ;
48425         
48426         this.selectedIndex = index;
48427         this.view.select(index);
48428         if(scrollIntoView !== false){
48429             var el = this.view.getNode(index);
48430             if(el){
48431                 this.innerList.scrollChildIntoView(el, false);
48432             }
48433         }
48434     },
48435
48436       
48437
48438     // private
48439     validateBlur : function(){
48440         
48441         return;
48442         
48443     },
48444
48445     // private
48446     initQuery : function(){
48447         this.doQuery(this.getRawValue());
48448     },
48449
48450     // private
48451     doForce : function(){
48452         if(this.el.dom.value.length > 0){
48453             this.el.dom.value =
48454                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48455              
48456         }
48457     },
48458
48459     /**
48460      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48461      * query allowing the query action to be canceled if needed.
48462      * @param {String} query The SQL query to execute
48463      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48464      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48465      * saved in the current store (defaults to false)
48466      */
48467     doQuery : function(q, forceAll){
48468         
48469         Roo.log('doQuery?');
48470         if(q === undefined || q === null){
48471             q = '';
48472         }
48473         var qe = {
48474             query: q,
48475             forceAll: forceAll,
48476             combo: this,
48477             cancel:false
48478         };
48479         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48480             return false;
48481         }
48482         q = qe.query;
48483         forceAll = qe.forceAll;
48484         if(forceAll === true || (q.length >= this.minChars)){
48485             if(this.lastQuery != q || this.alwaysQuery){
48486                 this.lastQuery = q;
48487                 if(this.mode == 'local'){
48488                     this.selectedIndex = -1;
48489                     if(forceAll){
48490                         this.store.clearFilter();
48491                     }else{
48492                         this.store.filter(this.displayField, q);
48493                     }
48494                     this.onLoad();
48495                 }else{
48496                     this.store.baseParams[this.queryParam] = q;
48497                     this.store.load({
48498                         params: this.getParams(q)
48499                     });
48500                     this.expand();
48501                 }
48502             }else{
48503                 this.selectedIndex = -1;
48504                 this.onLoad();   
48505             }
48506         }
48507     },
48508
48509     // private
48510     getParams : function(q){
48511         var p = {};
48512         //p[this.queryParam] = q;
48513         if(this.pageSize){
48514             p.start = 0;
48515             p.limit = this.pageSize;
48516         }
48517         return p;
48518     },
48519
48520     /**
48521      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48522      */
48523     collapse : function(){
48524         
48525     },
48526
48527     // private
48528     collapseIf : function(e){
48529         
48530     },
48531
48532     /**
48533      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48534      */
48535     expand : function(){
48536         
48537     } ,
48538
48539     // private
48540      
48541
48542     /** 
48543     * @cfg {Boolean} grow 
48544     * @hide 
48545     */
48546     /** 
48547     * @cfg {Number} growMin 
48548     * @hide 
48549     */
48550     /** 
48551     * @cfg {Number} growMax 
48552     * @hide 
48553     */
48554     /**
48555      * @hide
48556      * @method autoSize
48557      */
48558     
48559     setWidth : function()
48560     {
48561         
48562     },
48563     getResizeEl : function(){
48564         return this.el;
48565     }
48566 });//<script type="text/javasscript">
48567  
48568
48569 /**
48570  * @class Roo.DDView
48571  * A DnD enabled version of Roo.View.
48572  * @param {Element/String} container The Element in which to create the View.
48573  * @param {String} tpl The template string used to create the markup for each element of the View
48574  * @param {Object} config The configuration properties. These include all the config options of
48575  * {@link Roo.View} plus some specific to this class.<br>
48576  * <p>
48577  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48578  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48579  * <p>
48580  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48581 .x-view-drag-insert-above {
48582         border-top:1px dotted #3366cc;
48583 }
48584 .x-view-drag-insert-below {
48585         border-bottom:1px dotted #3366cc;
48586 }
48587 </code></pre>
48588  * 
48589  */
48590  
48591 Roo.DDView = function(container, tpl, config) {
48592     Roo.DDView.superclass.constructor.apply(this, arguments);
48593     this.getEl().setStyle("outline", "0px none");
48594     this.getEl().unselectable();
48595     if (this.dragGroup) {
48596                 this.setDraggable(this.dragGroup.split(","));
48597     }
48598     if (this.dropGroup) {
48599                 this.setDroppable(this.dropGroup.split(","));
48600     }
48601     if (this.deletable) {
48602         this.setDeletable();
48603     }
48604     this.isDirtyFlag = false;
48605         this.addEvents({
48606                 "drop" : true
48607         });
48608 };
48609
48610 Roo.extend(Roo.DDView, Roo.View, {
48611 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48612 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48613 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48614 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48615
48616         isFormField: true,
48617
48618         reset: Roo.emptyFn,
48619         
48620         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48621
48622         validate: function() {
48623                 return true;
48624         },
48625         
48626         destroy: function() {
48627                 this.purgeListeners();
48628                 this.getEl.removeAllListeners();
48629                 this.getEl().remove();
48630                 if (this.dragZone) {
48631                         if (this.dragZone.destroy) {
48632                                 this.dragZone.destroy();
48633                         }
48634                 }
48635                 if (this.dropZone) {
48636                         if (this.dropZone.destroy) {
48637                                 this.dropZone.destroy();
48638                         }
48639                 }
48640         },
48641
48642 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48643         getName: function() {
48644                 return this.name;
48645         },
48646
48647 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48648         setValue: function(v) {
48649                 if (!this.store) {
48650                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48651                 }
48652                 var data = {};
48653                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48654                 this.store.proxy = new Roo.data.MemoryProxy(data);
48655                 this.store.load();
48656         },
48657
48658 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48659         getValue: function() {
48660                 var result = '(';
48661                 this.store.each(function(rec) {
48662                         result += rec.id + ',';
48663                 });
48664                 return result.substr(0, result.length - 1) + ')';
48665         },
48666         
48667         getIds: function() {
48668                 var i = 0, result = new Array(this.store.getCount());
48669                 this.store.each(function(rec) {
48670                         result[i++] = rec.id;
48671                 });
48672                 return result;
48673         },
48674         
48675         isDirty: function() {
48676                 return this.isDirtyFlag;
48677         },
48678
48679 /**
48680  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48681  *      whole Element becomes the target, and this causes the drop gesture to append.
48682  */
48683     getTargetFromEvent : function(e) {
48684                 var target = e.getTarget();
48685                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48686                 target = target.parentNode;
48687                 }
48688                 if (!target) {
48689                         target = this.el.dom.lastChild || this.el.dom;
48690                 }
48691                 return target;
48692     },
48693
48694 /**
48695  *      Create the drag data which consists of an object which has the property "ddel" as
48696  *      the drag proxy element. 
48697  */
48698     getDragData : function(e) {
48699         var target = this.findItemFromChild(e.getTarget());
48700                 if(target) {
48701                         this.handleSelection(e);
48702                         var selNodes = this.getSelectedNodes();
48703             var dragData = {
48704                 source: this,
48705                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48706                 nodes: selNodes,
48707                 records: []
48708                         };
48709                         var selectedIndices = this.getSelectedIndexes();
48710                         for (var i = 0; i < selectedIndices.length; i++) {
48711                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48712                         }
48713                         if (selNodes.length == 1) {
48714                                 dragData.ddel = target.cloneNode(true); // the div element
48715                         } else {
48716                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48717                                 div.className = 'multi-proxy';
48718                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48719                                         div.appendChild(selNodes[i].cloneNode(true));
48720                                 }
48721                                 dragData.ddel = div;
48722                         }
48723             //console.log(dragData)
48724             //console.log(dragData.ddel.innerHTML)
48725                         return dragData;
48726                 }
48727         //console.log('nodragData')
48728                 return false;
48729     },
48730     
48731 /**     Specify to which ddGroup items in this DDView may be dragged. */
48732     setDraggable: function(ddGroup) {
48733         if (ddGroup instanceof Array) {
48734                 Roo.each(ddGroup, this.setDraggable, this);
48735                 return;
48736         }
48737         if (this.dragZone) {
48738                 this.dragZone.addToGroup(ddGroup);
48739         } else {
48740                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48741                                 containerScroll: true,
48742                                 ddGroup: ddGroup 
48743
48744                         });
48745 //                      Draggability implies selection. DragZone's mousedown selects the element.
48746                         if (!this.multiSelect) { this.singleSelect = true; }
48747
48748 //                      Wire the DragZone's handlers up to methods in *this*
48749                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48750                 }
48751     },
48752
48753 /**     Specify from which ddGroup this DDView accepts drops. */
48754     setDroppable: function(ddGroup) {
48755         if (ddGroup instanceof Array) {
48756                 Roo.each(ddGroup, this.setDroppable, this);
48757                 return;
48758         }
48759         if (this.dropZone) {
48760                 this.dropZone.addToGroup(ddGroup);
48761         } else {
48762                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48763                                 containerScroll: true,
48764                                 ddGroup: ddGroup
48765                         });
48766
48767 //                      Wire the DropZone's handlers up to methods in *this*
48768                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48769                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48770                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48771                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48772                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48773                 }
48774     },
48775
48776 /**     Decide whether to drop above or below a View node. */
48777     getDropPoint : function(e, n, dd){
48778         if (n == this.el.dom) { return "above"; }
48779                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48780                 var c = t + (b - t) / 2;
48781                 var y = Roo.lib.Event.getPageY(e);
48782                 if(y <= c) {
48783                         return "above";
48784                 }else{
48785                         return "below";
48786                 }
48787     },
48788
48789     onNodeEnter : function(n, dd, e, data){
48790                 return false;
48791     },
48792     
48793     onNodeOver : function(n, dd, e, data){
48794                 var pt = this.getDropPoint(e, n, dd);
48795                 // set the insert point style on the target node
48796                 var dragElClass = this.dropNotAllowed;
48797                 if (pt) {
48798                         var targetElClass;
48799                         if (pt == "above"){
48800                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48801                                 targetElClass = "x-view-drag-insert-above";
48802                         } else {
48803                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48804                                 targetElClass = "x-view-drag-insert-below";
48805                         }
48806                         if (this.lastInsertClass != targetElClass){
48807                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48808                                 this.lastInsertClass = targetElClass;
48809                         }
48810                 }
48811                 return dragElClass;
48812         },
48813
48814     onNodeOut : function(n, dd, e, data){
48815                 this.removeDropIndicators(n);
48816     },
48817
48818     onNodeDrop : function(n, dd, e, data){
48819         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48820                 return false;
48821         }
48822         var pt = this.getDropPoint(e, n, dd);
48823                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48824                 if (pt == "below") { insertAt++; }
48825                 for (var i = 0; i < data.records.length; i++) {
48826                         var r = data.records[i];
48827                         var dup = this.store.getById(r.id);
48828                         if (dup && (dd != this.dragZone)) {
48829                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48830                         } else {
48831                                 if (data.copy) {
48832                                         this.store.insert(insertAt++, r.copy());
48833                                 } else {
48834                                         data.source.isDirtyFlag = true;
48835                                         r.store.remove(r);
48836                                         this.store.insert(insertAt++, r);
48837                                 }
48838                                 this.isDirtyFlag = true;
48839                         }
48840                 }
48841                 this.dragZone.cachedTarget = null;
48842                 return true;
48843     },
48844
48845     removeDropIndicators : function(n){
48846                 if(n){
48847                         Roo.fly(n).removeClass([
48848                                 "x-view-drag-insert-above",
48849                                 "x-view-drag-insert-below"]);
48850                         this.lastInsertClass = "_noclass";
48851                 }
48852     },
48853
48854 /**
48855  *      Utility method. Add a delete option to the DDView's context menu.
48856  *      @param {String} imageUrl The URL of the "delete" icon image.
48857  */
48858         setDeletable: function(imageUrl) {
48859                 if (!this.singleSelect && !this.multiSelect) {
48860                         this.singleSelect = true;
48861                 }
48862                 var c = this.getContextMenu();
48863                 this.contextMenu.on("itemclick", function(item) {
48864                         switch (item.id) {
48865                                 case "delete":
48866                                         this.remove(this.getSelectedIndexes());
48867                                         break;
48868                         }
48869                 }, this);
48870                 this.contextMenu.add({
48871                         icon: imageUrl,
48872                         id: "delete",
48873                         text: 'Delete'
48874                 });
48875         },
48876         
48877 /**     Return the context menu for this DDView. */
48878         getContextMenu: function() {
48879                 if (!this.contextMenu) {
48880 //                      Create the View's context menu
48881                         this.contextMenu = new Roo.menu.Menu({
48882                                 id: this.id + "-contextmenu"
48883                         });
48884                         this.el.on("contextmenu", this.showContextMenu, this);
48885                 }
48886                 return this.contextMenu;
48887         },
48888         
48889         disableContextMenu: function() {
48890                 if (this.contextMenu) {
48891                         this.el.un("contextmenu", this.showContextMenu, this);
48892                 }
48893         },
48894
48895         showContextMenu: function(e, item) {
48896         item = this.findItemFromChild(e.getTarget());
48897                 if (item) {
48898                         e.stopEvent();
48899                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48900                         this.contextMenu.showAt(e.getXY());
48901             }
48902     },
48903
48904 /**
48905  *      Remove {@link Roo.data.Record}s at the specified indices.
48906  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48907  */
48908     remove: function(selectedIndices) {
48909                 selectedIndices = [].concat(selectedIndices);
48910                 for (var i = 0; i < selectedIndices.length; i++) {
48911                         var rec = this.store.getAt(selectedIndices[i]);
48912                         this.store.remove(rec);
48913                 }
48914     },
48915
48916 /**
48917  *      Double click fires the event, but also, if this is draggable, and there is only one other
48918  *      related DropZone, it transfers the selected node.
48919  */
48920     onDblClick : function(e){
48921         var item = this.findItemFromChild(e.getTarget());
48922         if(item){
48923             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48924                 return false;
48925             }
48926             if (this.dragGroup) {
48927                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48928                     while (targets.indexOf(this.dropZone) > -1) {
48929                             targets.remove(this.dropZone);
48930                                 }
48931                     if (targets.length == 1) {
48932                                         this.dragZone.cachedTarget = null;
48933                         var el = Roo.get(targets[0].getEl());
48934                         var box = el.getBox(true);
48935                         targets[0].onNodeDrop(el.dom, {
48936                                 target: el.dom,
48937                                 xy: [box.x, box.y + box.height - 1]
48938                         }, null, this.getDragData(e));
48939                     }
48940                 }
48941         }
48942     },
48943     
48944     handleSelection: function(e) {
48945                 this.dragZone.cachedTarget = null;
48946         var item = this.findItemFromChild(e.getTarget());
48947         if (!item) {
48948                 this.clearSelections(true);
48949                 return;
48950         }
48951                 if (item && (this.multiSelect || this.singleSelect)){
48952                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48953                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48954                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48955                                 this.unselect(item);
48956                         } else {
48957                                 this.select(item, this.multiSelect && e.ctrlKey);
48958                                 this.lastSelection = item;
48959                         }
48960                 }
48961     },
48962
48963     onItemClick : function(item, index, e){
48964                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48965                         return false;
48966                 }
48967                 return true;
48968     },
48969
48970     unselect : function(nodeInfo, suppressEvent){
48971                 var node = this.getNode(nodeInfo);
48972                 if(node && this.isSelected(node)){
48973                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48974                                 Roo.fly(node).removeClass(this.selectedClass);
48975                                 this.selections.remove(node);
48976                                 if(!suppressEvent){
48977                                         this.fireEvent("selectionchange", this, this.selections);
48978                                 }
48979                         }
48980                 }
48981     }
48982 });
48983 /*
48984  * Based on:
48985  * Ext JS Library 1.1.1
48986  * Copyright(c) 2006-2007, Ext JS, LLC.
48987  *
48988  * Originally Released Under LGPL - original licence link has changed is not relivant.
48989  *
48990  * Fork - LGPL
48991  * <script type="text/javascript">
48992  */
48993  
48994 /**
48995  * @class Roo.LayoutManager
48996  * @extends Roo.util.Observable
48997  * Base class for layout managers.
48998  */
48999 Roo.LayoutManager = function(container, config){
49000     Roo.LayoutManager.superclass.constructor.call(this);
49001     this.el = Roo.get(container);
49002     // ie scrollbar fix
49003     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49004         document.body.scroll = "no";
49005     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49006         this.el.position('relative');
49007     }
49008     this.id = this.el.id;
49009     this.el.addClass("x-layout-container");
49010     /** false to disable window resize monitoring @type Boolean */
49011     this.monitorWindowResize = true;
49012     this.regions = {};
49013     this.addEvents({
49014         /**
49015          * @event layout
49016          * Fires when a layout is performed. 
49017          * @param {Roo.LayoutManager} this
49018          */
49019         "layout" : true,
49020         /**
49021          * @event regionresized
49022          * Fires when the user resizes a region. 
49023          * @param {Roo.LayoutRegion} region The resized region
49024          * @param {Number} newSize The new size (width for east/west, height for north/south)
49025          */
49026         "regionresized" : true,
49027         /**
49028          * @event regioncollapsed
49029          * Fires when a region is collapsed. 
49030          * @param {Roo.LayoutRegion} region The collapsed region
49031          */
49032         "regioncollapsed" : true,
49033         /**
49034          * @event regionexpanded
49035          * Fires when a region is expanded.  
49036          * @param {Roo.LayoutRegion} region The expanded region
49037          */
49038         "regionexpanded" : true
49039     });
49040     this.updating = false;
49041     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49042 };
49043
49044 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49045     /**
49046      * Returns true if this layout is currently being updated
49047      * @return {Boolean}
49048      */
49049     isUpdating : function(){
49050         return this.updating; 
49051     },
49052     
49053     /**
49054      * Suspend the LayoutManager from doing auto-layouts while
49055      * making multiple add or remove calls
49056      */
49057     beginUpdate : function(){
49058         this.updating = true;    
49059     },
49060     
49061     /**
49062      * Restore auto-layouts and optionally disable the manager from performing a layout
49063      * @param {Boolean} noLayout true to disable a layout update 
49064      */
49065     endUpdate : function(noLayout){
49066         this.updating = false;
49067         if(!noLayout){
49068             this.layout();
49069         }    
49070     },
49071     
49072     layout: function(){
49073         
49074     },
49075     
49076     onRegionResized : function(region, newSize){
49077         this.fireEvent("regionresized", region, newSize);
49078         this.layout();
49079     },
49080     
49081     onRegionCollapsed : function(region){
49082         this.fireEvent("regioncollapsed", region);
49083     },
49084     
49085     onRegionExpanded : function(region){
49086         this.fireEvent("regionexpanded", region);
49087     },
49088         
49089     /**
49090      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49091      * performs box-model adjustments.
49092      * @return {Object} The size as an object {width: (the width), height: (the height)}
49093      */
49094     getViewSize : function(){
49095         var size;
49096         if(this.el.dom != document.body){
49097             size = this.el.getSize();
49098         }else{
49099             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49100         }
49101         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49102         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49103         return size;
49104     },
49105     
49106     /**
49107      * Returns the Element this layout is bound to.
49108      * @return {Roo.Element}
49109      */
49110     getEl : function(){
49111         return this.el;
49112     },
49113     
49114     /**
49115      * Returns the specified region.
49116      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49117      * @return {Roo.LayoutRegion}
49118      */
49119     getRegion : function(target){
49120         return this.regions[target.toLowerCase()];
49121     },
49122     
49123     onWindowResize : function(){
49124         if(this.monitorWindowResize){
49125             this.layout();
49126         }
49127     }
49128 });/*
49129  * Based on:
49130  * Ext JS Library 1.1.1
49131  * Copyright(c) 2006-2007, Ext JS, LLC.
49132  *
49133  * Originally Released Under LGPL - original licence link has changed is not relivant.
49134  *
49135  * Fork - LGPL
49136  * <script type="text/javascript">
49137  */
49138 /**
49139  * @class Roo.BorderLayout
49140  * @extends Roo.LayoutManager
49141  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49142  * please see: <br><br>
49143  * <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>
49144  * <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>
49145  * Example:
49146  <pre><code>
49147  var layout = new Roo.BorderLayout(document.body, {
49148     north: {
49149         initialSize: 25,
49150         titlebar: false
49151     },
49152     west: {
49153         split:true,
49154         initialSize: 200,
49155         minSize: 175,
49156         maxSize: 400,
49157         titlebar: true,
49158         collapsible: true
49159     },
49160     east: {
49161         split:true,
49162         initialSize: 202,
49163         minSize: 175,
49164         maxSize: 400,
49165         titlebar: true,
49166         collapsible: true
49167     },
49168     south: {
49169         split:true,
49170         initialSize: 100,
49171         minSize: 100,
49172         maxSize: 200,
49173         titlebar: true,
49174         collapsible: true
49175     },
49176     center: {
49177         titlebar: true,
49178         autoScroll:true,
49179         resizeTabs: true,
49180         minTabWidth: 50,
49181         preferredTabWidth: 150
49182     }
49183 });
49184
49185 // shorthand
49186 var CP = Roo.ContentPanel;
49187
49188 layout.beginUpdate();
49189 layout.add("north", new CP("north", "North"));
49190 layout.add("south", new CP("south", {title: "South", closable: true}));
49191 layout.add("west", new CP("west", {title: "West"}));
49192 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49193 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49194 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49195 layout.getRegion("center").showPanel("center1");
49196 layout.endUpdate();
49197 </code></pre>
49198
49199 <b>The container the layout is rendered into can be either the body element or any other element.
49200 If it is not the body element, the container needs to either be an absolute positioned element,
49201 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49202 the container size if it is not the body element.</b>
49203
49204 * @constructor
49205 * Create a new BorderLayout
49206 * @param {String/HTMLElement/Element} container The container this layout is bound to
49207 * @param {Object} config Configuration options
49208  */
49209 Roo.BorderLayout = function(container, config){
49210     config = config || {};
49211     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49212     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49213     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49214         var target = this.factory.validRegions[i];
49215         if(config[target]){
49216             this.addRegion(target, config[target]);
49217         }
49218     }
49219 };
49220
49221 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49222     /**
49223      * Creates and adds a new region if it doesn't already exist.
49224      * @param {String} target The target region key (north, south, east, west or center).
49225      * @param {Object} config The regions config object
49226      * @return {BorderLayoutRegion} The new region
49227      */
49228     addRegion : function(target, config){
49229         if(!this.regions[target]){
49230             var r = this.factory.create(target, this, config);
49231             this.bindRegion(target, r);
49232         }
49233         return this.regions[target];
49234     },
49235
49236     // private (kinda)
49237     bindRegion : function(name, r){
49238         this.regions[name] = r;
49239         r.on("visibilitychange", this.layout, this);
49240         r.on("paneladded", this.layout, this);
49241         r.on("panelremoved", this.layout, this);
49242         r.on("invalidated", this.layout, this);
49243         r.on("resized", this.onRegionResized, this);
49244         r.on("collapsed", this.onRegionCollapsed, this);
49245         r.on("expanded", this.onRegionExpanded, this);
49246     },
49247
49248     /**
49249      * Performs a layout update.
49250      */
49251     layout : function(){
49252         if(this.updating) return;
49253         var size = this.getViewSize();
49254         var w = size.width;
49255         var h = size.height;
49256         var centerW = w;
49257         var centerH = h;
49258         var centerY = 0;
49259         var centerX = 0;
49260         //var x = 0, y = 0;
49261
49262         var rs = this.regions;
49263         var north = rs["north"];
49264         var south = rs["south"]; 
49265         var west = rs["west"];
49266         var east = rs["east"];
49267         var center = rs["center"];
49268         //if(this.hideOnLayout){ // not supported anymore
49269             //c.el.setStyle("display", "none");
49270         //}
49271         if(north && north.isVisible()){
49272             var b = north.getBox();
49273             var m = north.getMargins();
49274             b.width = w - (m.left+m.right);
49275             b.x = m.left;
49276             b.y = m.top;
49277             centerY = b.height + b.y + m.bottom;
49278             centerH -= centerY;
49279             north.updateBox(this.safeBox(b));
49280         }
49281         if(south && south.isVisible()){
49282             var b = south.getBox();
49283             var m = south.getMargins();
49284             b.width = w - (m.left+m.right);
49285             b.x = m.left;
49286             var totalHeight = (b.height + m.top + m.bottom);
49287             b.y = h - totalHeight + m.top;
49288             centerH -= totalHeight;
49289             south.updateBox(this.safeBox(b));
49290         }
49291         if(west && west.isVisible()){
49292             var b = west.getBox();
49293             var m = west.getMargins();
49294             b.height = centerH - (m.top+m.bottom);
49295             b.x = m.left;
49296             b.y = centerY + m.top;
49297             var totalWidth = (b.width + m.left + m.right);
49298             centerX += totalWidth;
49299             centerW -= totalWidth;
49300             west.updateBox(this.safeBox(b));
49301         }
49302         if(east && east.isVisible()){
49303             var b = east.getBox();
49304             var m = east.getMargins();
49305             b.height = centerH - (m.top+m.bottom);
49306             var totalWidth = (b.width + m.left + m.right);
49307             b.x = w - totalWidth + m.left;
49308             b.y = centerY + m.top;
49309             centerW -= totalWidth;
49310             east.updateBox(this.safeBox(b));
49311         }
49312         if(center){
49313             var m = center.getMargins();
49314             var centerBox = {
49315                 x: centerX + m.left,
49316                 y: centerY + m.top,
49317                 width: centerW - (m.left+m.right),
49318                 height: centerH - (m.top+m.bottom)
49319             };
49320             //if(this.hideOnLayout){
49321                 //center.el.setStyle("display", "block");
49322             //}
49323             center.updateBox(this.safeBox(centerBox));
49324         }
49325         this.el.repaint();
49326         this.fireEvent("layout", this);
49327     },
49328
49329     // private
49330     safeBox : function(box){
49331         box.width = Math.max(0, box.width);
49332         box.height = Math.max(0, box.height);
49333         return box;
49334     },
49335
49336     /**
49337      * Adds a ContentPanel (or subclass) to this layout.
49338      * @param {String} target The target region key (north, south, east, west or center).
49339      * @param {Roo.ContentPanel} panel The panel to add
49340      * @return {Roo.ContentPanel} The added panel
49341      */
49342     add : function(target, panel){
49343          
49344         target = target.toLowerCase();
49345         return this.regions[target].add(panel);
49346     },
49347
49348     /**
49349      * Remove a ContentPanel (or subclass) to this layout.
49350      * @param {String} target The target region key (north, south, east, west or center).
49351      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49352      * @return {Roo.ContentPanel} The removed panel
49353      */
49354     remove : function(target, panel){
49355         target = target.toLowerCase();
49356         return this.regions[target].remove(panel);
49357     },
49358
49359     /**
49360      * Searches all regions for a panel with the specified id
49361      * @param {String} panelId
49362      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49363      */
49364     findPanel : function(panelId){
49365         var rs = this.regions;
49366         for(var target in rs){
49367             if(typeof rs[target] != "function"){
49368                 var p = rs[target].getPanel(panelId);
49369                 if(p){
49370                     return p;
49371                 }
49372             }
49373         }
49374         return null;
49375     },
49376
49377     /**
49378      * Searches all regions for a panel with the specified id and activates (shows) it.
49379      * @param {String/ContentPanel} panelId The panels id or the panel itself
49380      * @return {Roo.ContentPanel} The shown panel or null
49381      */
49382     showPanel : function(panelId) {
49383       var rs = this.regions;
49384       for(var target in rs){
49385          var r = rs[target];
49386          if(typeof r != "function"){
49387             if(r.hasPanel(panelId)){
49388                return r.showPanel(panelId);
49389             }
49390          }
49391       }
49392       return null;
49393    },
49394
49395    /**
49396      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49397      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49398      */
49399     restoreState : function(provider){
49400         if(!provider){
49401             provider = Roo.state.Manager;
49402         }
49403         var sm = new Roo.LayoutStateManager();
49404         sm.init(this, provider);
49405     },
49406
49407     /**
49408      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49409      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49410      * a valid ContentPanel config object.  Example:
49411      * <pre><code>
49412 // Create the main layout
49413 var layout = new Roo.BorderLayout('main-ct', {
49414     west: {
49415         split:true,
49416         minSize: 175,
49417         titlebar: true
49418     },
49419     center: {
49420         title:'Components'
49421     }
49422 }, 'main-ct');
49423
49424 // Create and add multiple ContentPanels at once via configs
49425 layout.batchAdd({
49426    west: {
49427        id: 'source-files',
49428        autoCreate:true,
49429        title:'Ext Source Files',
49430        autoScroll:true,
49431        fitToFrame:true
49432    },
49433    center : {
49434        el: cview,
49435        autoScroll:true,
49436        fitToFrame:true,
49437        toolbar: tb,
49438        resizeEl:'cbody'
49439    }
49440 });
49441 </code></pre>
49442      * @param {Object} regions An object containing ContentPanel configs by region name
49443      */
49444     batchAdd : function(regions){
49445         this.beginUpdate();
49446         for(var rname in regions){
49447             var lr = this.regions[rname];
49448             if(lr){
49449                 this.addTypedPanels(lr, regions[rname]);
49450             }
49451         }
49452         this.endUpdate();
49453     },
49454
49455     // private
49456     addTypedPanels : function(lr, ps){
49457         if(typeof ps == 'string'){
49458             lr.add(new Roo.ContentPanel(ps));
49459         }
49460         else if(ps instanceof Array){
49461             for(var i =0, len = ps.length; i < len; i++){
49462                 this.addTypedPanels(lr, ps[i]);
49463             }
49464         }
49465         else if(!ps.events){ // raw config?
49466             var el = ps.el;
49467             delete ps.el; // prevent conflict
49468             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49469         }
49470         else {  // panel object assumed!
49471             lr.add(ps);
49472         }
49473     },
49474     /**
49475      * Adds a xtype elements to the layout.
49476      * <pre><code>
49477
49478 layout.addxtype({
49479        xtype : 'ContentPanel',
49480        region: 'west',
49481        items: [ .... ]
49482    }
49483 );
49484
49485 layout.addxtype({
49486         xtype : 'NestedLayoutPanel',
49487         region: 'west',
49488         layout: {
49489            center: { },
49490            west: { }   
49491         },
49492         items : [ ... list of content panels or nested layout panels.. ]
49493    }
49494 );
49495 </code></pre>
49496      * @param {Object} cfg Xtype definition of item to add.
49497      */
49498     addxtype : function(cfg)
49499     {
49500         // basically accepts a pannel...
49501         // can accept a layout region..!?!?
49502         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49503         
49504         if (!cfg.xtype.match(/Panel$/)) {
49505             return false;
49506         }
49507         var ret = false;
49508         
49509         if (typeof(cfg.region) == 'undefined') {
49510             Roo.log("Failed to add Panel, region was not set");
49511             Roo.log(cfg);
49512             return false;
49513         }
49514         var region = cfg.region;
49515         delete cfg.region;
49516         
49517           
49518         var xitems = [];
49519         if (cfg.items) {
49520             xitems = cfg.items;
49521             delete cfg.items;
49522         }
49523         var nb = false;
49524         
49525         switch(cfg.xtype) 
49526         {
49527             case 'ContentPanel':  // ContentPanel (el, cfg)
49528             case 'ScrollPanel':  // ContentPanel (el, cfg)
49529             case 'ViewPanel': 
49530                 if(cfg.autoCreate) {
49531                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49532                 } else {
49533                     var el = this.el.createChild();
49534                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49535                 }
49536                 
49537                 this.add(region, ret);
49538                 break;
49539             
49540             
49541             case 'TreePanel': // our new panel!
49542                 cfg.el = this.el.createChild();
49543                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49544                 this.add(region, ret);
49545                 break;
49546             
49547             case 'NestedLayoutPanel': 
49548                 // create a new Layout (which is  a Border Layout...
49549                 var el = this.el.createChild();
49550                 var clayout = cfg.layout;
49551                 delete cfg.layout;
49552                 clayout.items   = clayout.items  || [];
49553                 // replace this exitems with the clayout ones..
49554                 xitems = clayout.items;
49555                  
49556                 
49557                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49558                     cfg.background = false;
49559                 }
49560                 var layout = new Roo.BorderLayout(el, clayout);
49561                 
49562                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49563                 //console.log('adding nested layout panel '  + cfg.toSource());
49564                 this.add(region, ret);
49565                 nb = {}; /// find first...
49566                 break;
49567                 
49568             case 'GridPanel': 
49569             
49570                 // needs grid and region
49571                 
49572                 //var el = this.getRegion(region).el.createChild();
49573                 var el = this.el.createChild();
49574                 // create the grid first...
49575                 
49576                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49577                 delete cfg.grid;
49578                 if (region == 'center' && this.active ) {
49579                     cfg.background = false;
49580                 }
49581                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49582                 
49583                 this.add(region, ret);
49584                 if (cfg.background) {
49585                     ret.on('activate', function(gp) {
49586                         if (!gp.grid.rendered) {
49587                             gp.grid.render();
49588                         }
49589                     });
49590                 } else {
49591                     grid.render();
49592                 }
49593                 break;
49594            
49595            
49596            
49597                 
49598                 
49599                 
49600             default:
49601                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49602                     
49603                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49604                     this.add(region, ret);
49605                 } else {
49606                 
49607                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49608                     return null;
49609                 }
49610                 
49611              // GridPanel (grid, cfg)
49612             
49613         }
49614         this.beginUpdate();
49615         // add children..
49616         var region = '';
49617         var abn = {};
49618         Roo.each(xitems, function(i)  {
49619             region = nb && i.region ? i.region : false;
49620             
49621             var add = ret.addxtype(i);
49622            
49623             if (region) {
49624                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49625                 if (!i.background) {
49626                     abn[region] = nb[region] ;
49627                 }
49628             }
49629             
49630         });
49631         this.endUpdate();
49632
49633         // make the last non-background panel active..
49634         //if (nb) { Roo.log(abn); }
49635         if (nb) {
49636             
49637             for(var r in abn) {
49638                 region = this.getRegion(r);
49639                 if (region) {
49640                     // tried using nb[r], but it does not work..
49641                      
49642                     region.showPanel(abn[r]);
49643                    
49644                 }
49645             }
49646         }
49647         return ret;
49648         
49649     }
49650 });
49651
49652 /**
49653  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49654  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49655  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49656  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49657  * <pre><code>
49658 // shorthand
49659 var CP = Roo.ContentPanel;
49660
49661 var layout = Roo.BorderLayout.create({
49662     north: {
49663         initialSize: 25,
49664         titlebar: false,
49665         panels: [new CP("north", "North")]
49666     },
49667     west: {
49668         split:true,
49669         initialSize: 200,
49670         minSize: 175,
49671         maxSize: 400,
49672         titlebar: true,
49673         collapsible: true,
49674         panels: [new CP("west", {title: "West"})]
49675     },
49676     east: {
49677         split:true,
49678         initialSize: 202,
49679         minSize: 175,
49680         maxSize: 400,
49681         titlebar: true,
49682         collapsible: true,
49683         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49684     },
49685     south: {
49686         split:true,
49687         initialSize: 100,
49688         minSize: 100,
49689         maxSize: 200,
49690         titlebar: true,
49691         collapsible: true,
49692         panels: [new CP("south", {title: "South", closable: true})]
49693     },
49694     center: {
49695         titlebar: true,
49696         autoScroll:true,
49697         resizeTabs: true,
49698         minTabWidth: 50,
49699         preferredTabWidth: 150,
49700         panels: [
49701             new CP("center1", {title: "Close Me", closable: true}),
49702             new CP("center2", {title: "Center Panel", closable: false})
49703         ]
49704     }
49705 }, document.body);
49706
49707 layout.getRegion("center").showPanel("center1");
49708 </code></pre>
49709  * @param config
49710  * @param targetEl
49711  */
49712 Roo.BorderLayout.create = function(config, targetEl){
49713     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49714     layout.beginUpdate();
49715     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49716     for(var j = 0, jlen = regions.length; j < jlen; j++){
49717         var lr = regions[j];
49718         if(layout.regions[lr] && config[lr].panels){
49719             var r = layout.regions[lr];
49720             var ps = config[lr].panels;
49721             layout.addTypedPanels(r, ps);
49722         }
49723     }
49724     layout.endUpdate();
49725     return layout;
49726 };
49727
49728 // private
49729 Roo.BorderLayout.RegionFactory = {
49730     // private
49731     validRegions : ["north","south","east","west","center"],
49732
49733     // private
49734     create : function(target, mgr, config){
49735         target = target.toLowerCase();
49736         if(config.lightweight || config.basic){
49737             return new Roo.BasicLayoutRegion(mgr, config, target);
49738         }
49739         switch(target){
49740             case "north":
49741                 return new Roo.NorthLayoutRegion(mgr, config);
49742             case "south":
49743                 return new Roo.SouthLayoutRegion(mgr, config);
49744             case "east":
49745                 return new Roo.EastLayoutRegion(mgr, config);
49746             case "west":
49747                 return new Roo.WestLayoutRegion(mgr, config);
49748             case "center":
49749                 return new Roo.CenterLayoutRegion(mgr, config);
49750         }
49751         throw 'Layout region "'+target+'" not supported.';
49752     }
49753 };/*
49754  * Based on:
49755  * Ext JS Library 1.1.1
49756  * Copyright(c) 2006-2007, Ext JS, LLC.
49757  *
49758  * Originally Released Under LGPL - original licence link has changed is not relivant.
49759  *
49760  * Fork - LGPL
49761  * <script type="text/javascript">
49762  */
49763  
49764 /**
49765  * @class Roo.BasicLayoutRegion
49766  * @extends Roo.util.Observable
49767  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49768  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49769  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49770  */
49771 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49772     this.mgr = mgr;
49773     this.position  = pos;
49774     this.events = {
49775         /**
49776          * @scope Roo.BasicLayoutRegion
49777          */
49778         
49779         /**
49780          * @event beforeremove
49781          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49782          * @param {Roo.LayoutRegion} this
49783          * @param {Roo.ContentPanel} panel The panel
49784          * @param {Object} e The cancel event object
49785          */
49786         "beforeremove" : true,
49787         /**
49788          * @event invalidated
49789          * Fires when the layout for this region is changed.
49790          * @param {Roo.LayoutRegion} this
49791          */
49792         "invalidated" : true,
49793         /**
49794          * @event visibilitychange
49795          * Fires when this region is shown or hidden 
49796          * @param {Roo.LayoutRegion} this
49797          * @param {Boolean} visibility true or false
49798          */
49799         "visibilitychange" : true,
49800         /**
49801          * @event paneladded
49802          * Fires when a panel is added. 
49803          * @param {Roo.LayoutRegion} this
49804          * @param {Roo.ContentPanel} panel The panel
49805          */
49806         "paneladded" : true,
49807         /**
49808          * @event panelremoved
49809          * Fires when a panel is removed. 
49810          * @param {Roo.LayoutRegion} this
49811          * @param {Roo.ContentPanel} panel The panel
49812          */
49813         "panelremoved" : true,
49814         /**
49815          * @event collapsed
49816          * Fires when this region is collapsed.
49817          * @param {Roo.LayoutRegion} this
49818          */
49819         "collapsed" : true,
49820         /**
49821          * @event expanded
49822          * Fires when this region is expanded.
49823          * @param {Roo.LayoutRegion} this
49824          */
49825         "expanded" : true,
49826         /**
49827          * @event slideshow
49828          * Fires when this region is slid into view.
49829          * @param {Roo.LayoutRegion} this
49830          */
49831         "slideshow" : true,
49832         /**
49833          * @event slidehide
49834          * Fires when this region slides out of view. 
49835          * @param {Roo.LayoutRegion} this
49836          */
49837         "slidehide" : true,
49838         /**
49839          * @event panelactivated
49840          * Fires when a panel is activated. 
49841          * @param {Roo.LayoutRegion} this
49842          * @param {Roo.ContentPanel} panel The activated panel
49843          */
49844         "panelactivated" : true,
49845         /**
49846          * @event resized
49847          * Fires when the user resizes this region. 
49848          * @param {Roo.LayoutRegion} this
49849          * @param {Number} newSize The new size (width for east/west, height for north/south)
49850          */
49851         "resized" : true
49852     };
49853     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49854     this.panels = new Roo.util.MixedCollection();
49855     this.panels.getKey = this.getPanelId.createDelegate(this);
49856     this.box = null;
49857     this.activePanel = null;
49858     // ensure listeners are added...
49859     
49860     if (config.listeners || config.events) {
49861         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49862             listeners : config.listeners || {},
49863             events : config.events || {}
49864         });
49865     }
49866     
49867     if(skipConfig !== true){
49868         this.applyConfig(config);
49869     }
49870 };
49871
49872 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49873     getPanelId : function(p){
49874         return p.getId();
49875     },
49876     
49877     applyConfig : function(config){
49878         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49879         this.config = config;
49880         
49881     },
49882     
49883     /**
49884      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49885      * the width, for horizontal (north, south) the height.
49886      * @param {Number} newSize The new width or height
49887      */
49888     resizeTo : function(newSize){
49889         var el = this.el ? this.el :
49890                  (this.activePanel ? this.activePanel.getEl() : null);
49891         if(el){
49892             switch(this.position){
49893                 case "east":
49894                 case "west":
49895                     el.setWidth(newSize);
49896                     this.fireEvent("resized", this, newSize);
49897                 break;
49898                 case "north":
49899                 case "south":
49900                     el.setHeight(newSize);
49901                     this.fireEvent("resized", this, newSize);
49902                 break;                
49903             }
49904         }
49905     },
49906     
49907     getBox : function(){
49908         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49909     },
49910     
49911     getMargins : function(){
49912         return this.margins;
49913     },
49914     
49915     updateBox : function(box){
49916         this.box = box;
49917         var el = this.activePanel.getEl();
49918         el.dom.style.left = box.x + "px";
49919         el.dom.style.top = box.y + "px";
49920         this.activePanel.setSize(box.width, box.height);
49921     },
49922     
49923     /**
49924      * Returns the container element for this region.
49925      * @return {Roo.Element}
49926      */
49927     getEl : function(){
49928         return this.activePanel;
49929     },
49930     
49931     /**
49932      * Returns true if this region is currently visible.
49933      * @return {Boolean}
49934      */
49935     isVisible : function(){
49936         return this.activePanel ? true : false;
49937     },
49938     
49939     setActivePanel : function(panel){
49940         panel = this.getPanel(panel);
49941         if(this.activePanel && this.activePanel != panel){
49942             this.activePanel.setActiveState(false);
49943             this.activePanel.getEl().setLeftTop(-10000,-10000);
49944         }
49945         this.activePanel = panel;
49946         panel.setActiveState(true);
49947         if(this.box){
49948             panel.setSize(this.box.width, this.box.height);
49949         }
49950         this.fireEvent("panelactivated", this, panel);
49951         this.fireEvent("invalidated");
49952     },
49953     
49954     /**
49955      * Show the specified panel.
49956      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49957      * @return {Roo.ContentPanel} The shown panel or null
49958      */
49959     showPanel : function(panel){
49960         if(panel = this.getPanel(panel)){
49961             this.setActivePanel(panel);
49962         }
49963         return panel;
49964     },
49965     
49966     /**
49967      * Get the active panel for this region.
49968      * @return {Roo.ContentPanel} The active panel or null
49969      */
49970     getActivePanel : function(){
49971         return this.activePanel;
49972     },
49973     
49974     /**
49975      * Add the passed ContentPanel(s)
49976      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49977      * @return {Roo.ContentPanel} The panel added (if only one was added)
49978      */
49979     add : function(panel){
49980         if(arguments.length > 1){
49981             for(var i = 0, len = arguments.length; i < len; i++) {
49982                 this.add(arguments[i]);
49983             }
49984             return null;
49985         }
49986         if(this.hasPanel(panel)){
49987             this.showPanel(panel);
49988             return panel;
49989         }
49990         var el = panel.getEl();
49991         if(el.dom.parentNode != this.mgr.el.dom){
49992             this.mgr.el.dom.appendChild(el.dom);
49993         }
49994         if(panel.setRegion){
49995             panel.setRegion(this);
49996         }
49997         this.panels.add(panel);
49998         el.setStyle("position", "absolute");
49999         if(!panel.background){
50000             this.setActivePanel(panel);
50001             if(this.config.initialSize && this.panels.getCount()==1){
50002                 this.resizeTo(this.config.initialSize);
50003             }
50004         }
50005         this.fireEvent("paneladded", this, panel);
50006         return panel;
50007     },
50008     
50009     /**
50010      * Returns true if the panel is in this region.
50011      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50012      * @return {Boolean}
50013      */
50014     hasPanel : function(panel){
50015         if(typeof panel == "object"){ // must be panel obj
50016             panel = panel.getId();
50017         }
50018         return this.getPanel(panel) ? true : false;
50019     },
50020     
50021     /**
50022      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50023      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50024      * @param {Boolean} preservePanel Overrides the config preservePanel option
50025      * @return {Roo.ContentPanel} The panel that was removed
50026      */
50027     remove : function(panel, preservePanel){
50028         panel = this.getPanel(panel);
50029         if(!panel){
50030             return null;
50031         }
50032         var e = {};
50033         this.fireEvent("beforeremove", this, panel, e);
50034         if(e.cancel === true){
50035             return null;
50036         }
50037         var panelId = panel.getId();
50038         this.panels.removeKey(panelId);
50039         return panel;
50040     },
50041     
50042     /**
50043      * Returns the panel specified or null if it's not in this region.
50044      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50045      * @return {Roo.ContentPanel}
50046      */
50047     getPanel : function(id){
50048         if(typeof id == "object"){ // must be panel obj
50049             return id;
50050         }
50051         return this.panels.get(id);
50052     },
50053     
50054     /**
50055      * Returns this regions position (north/south/east/west/center).
50056      * @return {String} 
50057      */
50058     getPosition: function(){
50059         return this.position;    
50060     }
50061 });/*
50062  * Based on:
50063  * Ext JS Library 1.1.1
50064  * Copyright(c) 2006-2007, Ext JS, LLC.
50065  *
50066  * Originally Released Under LGPL - original licence link has changed is not relivant.
50067  *
50068  * Fork - LGPL
50069  * <script type="text/javascript">
50070  */
50071  
50072 /**
50073  * @class Roo.LayoutRegion
50074  * @extends Roo.BasicLayoutRegion
50075  * This class represents a region in a layout manager.
50076  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50077  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50078  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50079  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50080  * @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})
50081  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50082  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50083  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50084  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50085  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50086  * @cfg {String}    title           The title for the region (overrides panel titles)
50087  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50088  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50089  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50090  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50091  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50092  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50093  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50094  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50095  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50096  * @cfg {Boolean}   showPin         True to show a pin button
50097  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50098  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50099  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50100  * @cfg {Number}    width           For East/West panels
50101  * @cfg {Number}    height          For North/South panels
50102  * @cfg {Boolean}   split           To show the splitter
50103  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50104  */
50105 Roo.LayoutRegion = function(mgr, config, pos){
50106     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50107     var dh = Roo.DomHelper;
50108     /** This region's container element 
50109     * @type Roo.Element */
50110     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50111     /** This region's title element 
50112     * @type Roo.Element */
50113
50114     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50115         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50116         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50117     ]}, true);
50118     this.titleEl.enableDisplayMode();
50119     /** This region's title text element 
50120     * @type HTMLElement */
50121     this.titleTextEl = this.titleEl.dom.firstChild;
50122     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50123     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50124     this.closeBtn.enableDisplayMode();
50125     this.closeBtn.on("click", this.closeClicked, this);
50126     this.closeBtn.hide();
50127
50128     this.createBody(config);
50129     this.visible = true;
50130     this.collapsed = false;
50131
50132     if(config.hideWhenEmpty){
50133         this.hide();
50134         this.on("paneladded", this.validateVisibility, this);
50135         this.on("panelremoved", this.validateVisibility, this);
50136     }
50137     this.applyConfig(config);
50138 };
50139
50140 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50141
50142     createBody : function(){
50143         /** This region's body element 
50144         * @type Roo.Element */
50145         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50146     },
50147
50148     applyConfig : function(c){
50149         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50150             var dh = Roo.DomHelper;
50151             if(c.titlebar !== false){
50152                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50153                 this.collapseBtn.on("click", this.collapse, this);
50154                 this.collapseBtn.enableDisplayMode();
50155
50156                 if(c.showPin === true || this.showPin){
50157                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50158                     this.stickBtn.enableDisplayMode();
50159                     this.stickBtn.on("click", this.expand, this);
50160                     this.stickBtn.hide();
50161                 }
50162             }
50163             /** This region's collapsed element
50164             * @type Roo.Element */
50165             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50166                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50167             ]}, true);
50168             if(c.floatable !== false){
50169                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50170                this.collapsedEl.on("click", this.collapseClick, this);
50171             }
50172
50173             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50174                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50175                    id: "message", unselectable: "on", style:{"float":"left"}});
50176                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50177              }
50178             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50179             this.expandBtn.on("click", this.expand, this);
50180         }
50181         if(this.collapseBtn){
50182             this.collapseBtn.setVisible(c.collapsible == true);
50183         }
50184         this.cmargins = c.cmargins || this.cmargins ||
50185                          (this.position == "west" || this.position == "east" ?
50186                              {top: 0, left: 2, right:2, bottom: 0} :
50187                              {top: 2, left: 0, right:0, bottom: 2});
50188         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50189         this.bottomTabs = c.tabPosition != "top";
50190         this.autoScroll = c.autoScroll || false;
50191         if(this.autoScroll){
50192             this.bodyEl.setStyle("overflow", "auto");
50193         }else{
50194             this.bodyEl.setStyle("overflow", "hidden");
50195         }
50196         //if(c.titlebar !== false){
50197             if((!c.titlebar && !c.title) || c.titlebar === false){
50198                 this.titleEl.hide();
50199             }else{
50200                 this.titleEl.show();
50201                 if(c.title){
50202                     this.titleTextEl.innerHTML = c.title;
50203                 }
50204             }
50205         //}
50206         this.duration = c.duration || .30;
50207         this.slideDuration = c.slideDuration || .45;
50208         this.config = c;
50209         if(c.collapsed){
50210             this.collapse(true);
50211         }
50212         if(c.hidden){
50213             this.hide();
50214         }
50215     },
50216     /**
50217      * Returns true if this region is currently visible.
50218      * @return {Boolean}
50219      */
50220     isVisible : function(){
50221         return this.visible;
50222     },
50223
50224     /**
50225      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50226      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50227      */
50228     setCollapsedTitle : function(title){
50229         title = title || "&#160;";
50230         if(this.collapsedTitleTextEl){
50231             this.collapsedTitleTextEl.innerHTML = title;
50232         }
50233     },
50234
50235     getBox : function(){
50236         var b;
50237         if(!this.collapsed){
50238             b = this.el.getBox(false, true);
50239         }else{
50240             b = this.collapsedEl.getBox(false, true);
50241         }
50242         return b;
50243     },
50244
50245     getMargins : function(){
50246         return this.collapsed ? this.cmargins : this.margins;
50247     },
50248
50249     highlight : function(){
50250         this.el.addClass("x-layout-panel-dragover");
50251     },
50252
50253     unhighlight : function(){
50254         this.el.removeClass("x-layout-panel-dragover");
50255     },
50256
50257     updateBox : function(box){
50258         this.box = box;
50259         if(!this.collapsed){
50260             this.el.dom.style.left = box.x + "px";
50261             this.el.dom.style.top = box.y + "px";
50262             this.updateBody(box.width, box.height);
50263         }else{
50264             this.collapsedEl.dom.style.left = box.x + "px";
50265             this.collapsedEl.dom.style.top = box.y + "px";
50266             this.collapsedEl.setSize(box.width, box.height);
50267         }
50268         if(this.tabs){
50269             this.tabs.autoSizeTabs();
50270         }
50271     },
50272
50273     updateBody : function(w, h){
50274         if(w !== null){
50275             this.el.setWidth(w);
50276             w -= this.el.getBorderWidth("rl");
50277             if(this.config.adjustments){
50278                 w += this.config.adjustments[0];
50279             }
50280         }
50281         if(h !== null){
50282             this.el.setHeight(h);
50283             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50284             h -= this.el.getBorderWidth("tb");
50285             if(this.config.adjustments){
50286                 h += this.config.adjustments[1];
50287             }
50288             this.bodyEl.setHeight(h);
50289             if(this.tabs){
50290                 h = this.tabs.syncHeight(h);
50291             }
50292         }
50293         if(this.panelSize){
50294             w = w !== null ? w : this.panelSize.width;
50295             h = h !== null ? h : this.panelSize.height;
50296         }
50297         if(this.activePanel){
50298             var el = this.activePanel.getEl();
50299             w = w !== null ? w : el.getWidth();
50300             h = h !== null ? h : el.getHeight();
50301             this.panelSize = {width: w, height: h};
50302             this.activePanel.setSize(w, h);
50303         }
50304         if(Roo.isIE && this.tabs){
50305             this.tabs.el.repaint();
50306         }
50307     },
50308
50309     /**
50310      * Returns the container element for this region.
50311      * @return {Roo.Element}
50312      */
50313     getEl : function(){
50314         return this.el;
50315     },
50316
50317     /**
50318      * Hides this region.
50319      */
50320     hide : function(){
50321         if(!this.collapsed){
50322             this.el.dom.style.left = "-2000px";
50323             this.el.hide();
50324         }else{
50325             this.collapsedEl.dom.style.left = "-2000px";
50326             this.collapsedEl.hide();
50327         }
50328         this.visible = false;
50329         this.fireEvent("visibilitychange", this, false);
50330     },
50331
50332     /**
50333      * Shows this region if it was previously hidden.
50334      */
50335     show : function(){
50336         if(!this.collapsed){
50337             this.el.show();
50338         }else{
50339             this.collapsedEl.show();
50340         }
50341         this.visible = true;
50342         this.fireEvent("visibilitychange", this, true);
50343     },
50344
50345     closeClicked : function(){
50346         if(this.activePanel){
50347             this.remove(this.activePanel);
50348         }
50349     },
50350
50351     collapseClick : function(e){
50352         if(this.isSlid){
50353            e.stopPropagation();
50354            this.slideIn();
50355         }else{
50356            e.stopPropagation();
50357            this.slideOut();
50358         }
50359     },
50360
50361     /**
50362      * Collapses this region.
50363      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50364      */
50365     collapse : function(skipAnim){
50366         if(this.collapsed) return;
50367         this.collapsed = true;
50368         if(this.split){
50369             this.split.el.hide();
50370         }
50371         if(this.config.animate && skipAnim !== true){
50372             this.fireEvent("invalidated", this);
50373             this.animateCollapse();
50374         }else{
50375             this.el.setLocation(-20000,-20000);
50376             this.el.hide();
50377             this.collapsedEl.show();
50378             this.fireEvent("collapsed", this);
50379             this.fireEvent("invalidated", this);
50380         }
50381     },
50382
50383     animateCollapse : function(){
50384         // overridden
50385     },
50386
50387     /**
50388      * Expands this region if it was previously collapsed.
50389      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50390      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50391      */
50392     expand : function(e, skipAnim){
50393         if(e) e.stopPropagation();
50394         if(!this.collapsed || this.el.hasActiveFx()) return;
50395         if(this.isSlid){
50396             this.afterSlideIn();
50397             skipAnim = true;
50398         }
50399         this.collapsed = false;
50400         if(this.config.animate && skipAnim !== true){
50401             this.animateExpand();
50402         }else{
50403             this.el.show();
50404             if(this.split){
50405                 this.split.el.show();
50406             }
50407             this.collapsedEl.setLocation(-2000,-2000);
50408             this.collapsedEl.hide();
50409             this.fireEvent("invalidated", this);
50410             this.fireEvent("expanded", this);
50411         }
50412     },
50413
50414     animateExpand : function(){
50415         // overridden
50416     },
50417
50418     initTabs : function()
50419     {
50420         this.bodyEl.setStyle("overflow", "hidden");
50421         var ts = new Roo.TabPanel(
50422                 this.bodyEl.dom,
50423                 {
50424                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50425                     disableTooltips: this.config.disableTabTips,
50426                     toolbar : this.config.toolbar
50427                 }
50428         );
50429         if(this.config.hideTabs){
50430             ts.stripWrap.setDisplayed(false);
50431         }
50432         this.tabs = ts;
50433         ts.resizeTabs = this.config.resizeTabs === true;
50434         ts.minTabWidth = this.config.minTabWidth || 40;
50435         ts.maxTabWidth = this.config.maxTabWidth || 250;
50436         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50437         ts.monitorResize = false;
50438         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50439         ts.bodyEl.addClass('x-layout-tabs-body');
50440         this.panels.each(this.initPanelAsTab, this);
50441     },
50442
50443     initPanelAsTab : function(panel){
50444         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50445                     this.config.closeOnTab && panel.isClosable());
50446         if(panel.tabTip !== undefined){
50447             ti.setTooltip(panel.tabTip);
50448         }
50449         ti.on("activate", function(){
50450               this.setActivePanel(panel);
50451         }, this);
50452         if(this.config.closeOnTab){
50453             ti.on("beforeclose", function(t, e){
50454                 e.cancel = true;
50455                 this.remove(panel);
50456             }, this);
50457         }
50458         return ti;
50459     },
50460
50461     updatePanelTitle : function(panel, title){
50462         if(this.activePanel == panel){
50463             this.updateTitle(title);
50464         }
50465         if(this.tabs){
50466             var ti = this.tabs.getTab(panel.getEl().id);
50467             ti.setText(title);
50468             if(panel.tabTip !== undefined){
50469                 ti.setTooltip(panel.tabTip);
50470             }
50471         }
50472     },
50473
50474     updateTitle : function(title){
50475         if(this.titleTextEl && !this.config.title){
50476             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50477         }
50478     },
50479
50480     setActivePanel : function(panel){
50481         panel = this.getPanel(panel);
50482         if(this.activePanel && this.activePanel != panel){
50483             this.activePanel.setActiveState(false);
50484         }
50485         this.activePanel = panel;
50486         panel.setActiveState(true);
50487         if(this.panelSize){
50488             panel.setSize(this.panelSize.width, this.panelSize.height);
50489         }
50490         if(this.closeBtn){
50491             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50492         }
50493         this.updateTitle(panel.getTitle());
50494         if(this.tabs){
50495             this.fireEvent("invalidated", this);
50496         }
50497         this.fireEvent("panelactivated", this, panel);
50498     },
50499
50500     /**
50501      * Shows the specified panel.
50502      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50503      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50504      */
50505     showPanel : function(panel)
50506     {
50507         panel = this.getPanel(panel);
50508         if(panel){
50509             if(this.tabs){
50510                 var tab = this.tabs.getTab(panel.getEl().id);
50511                 if(tab.isHidden()){
50512                     this.tabs.unhideTab(tab.id);
50513                 }
50514                 tab.activate();
50515             }else{
50516                 this.setActivePanel(panel);
50517             }
50518         }
50519         return panel;
50520     },
50521
50522     /**
50523      * Get the active panel for this region.
50524      * @return {Roo.ContentPanel} The active panel or null
50525      */
50526     getActivePanel : function(){
50527         return this.activePanel;
50528     },
50529
50530     validateVisibility : function(){
50531         if(this.panels.getCount() < 1){
50532             this.updateTitle("&#160;");
50533             this.closeBtn.hide();
50534             this.hide();
50535         }else{
50536             if(!this.isVisible()){
50537                 this.show();
50538             }
50539         }
50540     },
50541
50542     /**
50543      * Adds the passed ContentPanel(s) to this region.
50544      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50545      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50546      */
50547     add : function(panel){
50548         if(arguments.length > 1){
50549             for(var i = 0, len = arguments.length; i < len; i++) {
50550                 this.add(arguments[i]);
50551             }
50552             return null;
50553         }
50554         if(this.hasPanel(panel)){
50555             this.showPanel(panel);
50556             return panel;
50557         }
50558         panel.setRegion(this);
50559         this.panels.add(panel);
50560         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50561             this.bodyEl.dom.appendChild(panel.getEl().dom);
50562             if(panel.background !== true){
50563                 this.setActivePanel(panel);
50564             }
50565             this.fireEvent("paneladded", this, panel);
50566             return panel;
50567         }
50568         if(!this.tabs){
50569             this.initTabs();
50570         }else{
50571             this.initPanelAsTab(panel);
50572         }
50573         if(panel.background !== true){
50574             this.tabs.activate(panel.getEl().id);
50575         }
50576         this.fireEvent("paneladded", this, panel);
50577         return panel;
50578     },
50579
50580     /**
50581      * Hides the tab for the specified panel.
50582      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50583      */
50584     hidePanel : function(panel){
50585         if(this.tabs && (panel = this.getPanel(panel))){
50586             this.tabs.hideTab(panel.getEl().id);
50587         }
50588     },
50589
50590     /**
50591      * Unhides the tab for a previously hidden panel.
50592      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50593      */
50594     unhidePanel : function(panel){
50595         if(this.tabs && (panel = this.getPanel(panel))){
50596             this.tabs.unhideTab(panel.getEl().id);
50597         }
50598     },
50599
50600     clearPanels : function(){
50601         while(this.panels.getCount() > 0){
50602              this.remove(this.panels.first());
50603         }
50604     },
50605
50606     /**
50607      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50608      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50609      * @param {Boolean} preservePanel Overrides the config preservePanel option
50610      * @return {Roo.ContentPanel} The panel that was removed
50611      */
50612     remove : function(panel, preservePanel){
50613         panel = this.getPanel(panel);
50614         if(!panel){
50615             return null;
50616         }
50617         var e = {};
50618         this.fireEvent("beforeremove", this, panel, e);
50619         if(e.cancel === true){
50620             return null;
50621         }
50622         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50623         var panelId = panel.getId();
50624         this.panels.removeKey(panelId);
50625         if(preservePanel){
50626             document.body.appendChild(panel.getEl().dom);
50627         }
50628         if(this.tabs){
50629             this.tabs.removeTab(panel.getEl().id);
50630         }else if (!preservePanel){
50631             this.bodyEl.dom.removeChild(panel.getEl().dom);
50632         }
50633         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50634             var p = this.panels.first();
50635             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50636             tempEl.appendChild(p.getEl().dom);
50637             this.bodyEl.update("");
50638             this.bodyEl.dom.appendChild(p.getEl().dom);
50639             tempEl = null;
50640             this.updateTitle(p.getTitle());
50641             this.tabs = null;
50642             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50643             this.setActivePanel(p);
50644         }
50645         panel.setRegion(null);
50646         if(this.activePanel == panel){
50647             this.activePanel = null;
50648         }
50649         if(this.config.autoDestroy !== false && preservePanel !== true){
50650             try{panel.destroy();}catch(e){}
50651         }
50652         this.fireEvent("panelremoved", this, panel);
50653         return panel;
50654     },
50655
50656     /**
50657      * Returns the TabPanel component used by this region
50658      * @return {Roo.TabPanel}
50659      */
50660     getTabs : function(){
50661         return this.tabs;
50662     },
50663
50664     createTool : function(parentEl, className){
50665         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50666             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50667         btn.addClassOnOver("x-layout-tools-button-over");
50668         return btn;
50669     }
50670 });/*
50671  * Based on:
50672  * Ext JS Library 1.1.1
50673  * Copyright(c) 2006-2007, Ext JS, LLC.
50674  *
50675  * Originally Released Under LGPL - original licence link has changed is not relivant.
50676  *
50677  * Fork - LGPL
50678  * <script type="text/javascript">
50679  */
50680  
50681
50682
50683 /**
50684  * @class Roo.SplitLayoutRegion
50685  * @extends Roo.LayoutRegion
50686  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50687  */
50688 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50689     this.cursor = cursor;
50690     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50691 };
50692
50693 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50694     splitTip : "Drag to resize.",
50695     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50696     useSplitTips : false,
50697
50698     applyConfig : function(config){
50699         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50700         if(config.split){
50701             if(!this.split){
50702                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50703                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50704                 /** The SplitBar for this region 
50705                 * @type Roo.SplitBar */
50706                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50707                 this.split.on("moved", this.onSplitMove, this);
50708                 this.split.useShim = config.useShim === true;
50709                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50710                 if(this.useSplitTips){
50711                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50712                 }
50713                 if(config.collapsible){
50714                     this.split.el.on("dblclick", this.collapse,  this);
50715                 }
50716             }
50717             if(typeof config.minSize != "undefined"){
50718                 this.split.minSize = config.minSize;
50719             }
50720             if(typeof config.maxSize != "undefined"){
50721                 this.split.maxSize = config.maxSize;
50722             }
50723             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50724                 this.hideSplitter();
50725             }
50726         }
50727     },
50728
50729     getHMaxSize : function(){
50730          var cmax = this.config.maxSize || 10000;
50731          var center = this.mgr.getRegion("center");
50732          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50733     },
50734
50735     getVMaxSize : function(){
50736          var cmax = this.config.maxSize || 10000;
50737          var center = this.mgr.getRegion("center");
50738          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50739     },
50740
50741     onSplitMove : function(split, newSize){
50742         this.fireEvent("resized", this, newSize);
50743     },
50744     
50745     /** 
50746      * Returns the {@link Roo.SplitBar} for this region.
50747      * @return {Roo.SplitBar}
50748      */
50749     getSplitBar : function(){
50750         return this.split;
50751     },
50752     
50753     hide : function(){
50754         this.hideSplitter();
50755         Roo.SplitLayoutRegion.superclass.hide.call(this);
50756     },
50757
50758     hideSplitter : function(){
50759         if(this.split){
50760             this.split.el.setLocation(-2000,-2000);
50761             this.split.el.hide();
50762         }
50763     },
50764
50765     show : function(){
50766         if(this.split){
50767             this.split.el.show();
50768         }
50769         Roo.SplitLayoutRegion.superclass.show.call(this);
50770     },
50771     
50772     beforeSlide: function(){
50773         if(Roo.isGecko){// firefox overflow auto bug workaround
50774             this.bodyEl.clip();
50775             if(this.tabs) this.tabs.bodyEl.clip();
50776             if(this.activePanel){
50777                 this.activePanel.getEl().clip();
50778                 
50779                 if(this.activePanel.beforeSlide){
50780                     this.activePanel.beforeSlide();
50781                 }
50782             }
50783         }
50784     },
50785     
50786     afterSlide : function(){
50787         if(Roo.isGecko){// firefox overflow auto bug workaround
50788             this.bodyEl.unclip();
50789             if(this.tabs) this.tabs.bodyEl.unclip();
50790             if(this.activePanel){
50791                 this.activePanel.getEl().unclip();
50792                 if(this.activePanel.afterSlide){
50793                     this.activePanel.afterSlide();
50794                 }
50795             }
50796         }
50797     },
50798
50799     initAutoHide : function(){
50800         if(this.autoHide !== false){
50801             if(!this.autoHideHd){
50802                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50803                 this.autoHideHd = {
50804                     "mouseout": function(e){
50805                         if(!e.within(this.el, true)){
50806                             st.delay(500);
50807                         }
50808                     },
50809                     "mouseover" : function(e){
50810                         st.cancel();
50811                     },
50812                     scope : this
50813                 };
50814             }
50815             this.el.on(this.autoHideHd);
50816         }
50817     },
50818
50819     clearAutoHide : function(){
50820         if(this.autoHide !== false){
50821             this.el.un("mouseout", this.autoHideHd.mouseout);
50822             this.el.un("mouseover", this.autoHideHd.mouseover);
50823         }
50824     },
50825
50826     clearMonitor : function(){
50827         Roo.get(document).un("click", this.slideInIf, this);
50828     },
50829
50830     // these names are backwards but not changed for compat
50831     slideOut : function(){
50832         if(this.isSlid || this.el.hasActiveFx()){
50833             return;
50834         }
50835         this.isSlid = true;
50836         if(this.collapseBtn){
50837             this.collapseBtn.hide();
50838         }
50839         this.closeBtnState = this.closeBtn.getStyle('display');
50840         this.closeBtn.hide();
50841         if(this.stickBtn){
50842             this.stickBtn.show();
50843         }
50844         this.el.show();
50845         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50846         this.beforeSlide();
50847         this.el.setStyle("z-index", 10001);
50848         this.el.slideIn(this.getSlideAnchor(), {
50849             callback: function(){
50850                 this.afterSlide();
50851                 this.initAutoHide();
50852                 Roo.get(document).on("click", this.slideInIf, this);
50853                 this.fireEvent("slideshow", this);
50854             },
50855             scope: this,
50856             block: true
50857         });
50858     },
50859
50860     afterSlideIn : function(){
50861         this.clearAutoHide();
50862         this.isSlid = false;
50863         this.clearMonitor();
50864         this.el.setStyle("z-index", "");
50865         if(this.collapseBtn){
50866             this.collapseBtn.show();
50867         }
50868         this.closeBtn.setStyle('display', this.closeBtnState);
50869         if(this.stickBtn){
50870             this.stickBtn.hide();
50871         }
50872         this.fireEvent("slidehide", this);
50873     },
50874
50875     slideIn : function(cb){
50876         if(!this.isSlid || this.el.hasActiveFx()){
50877             Roo.callback(cb);
50878             return;
50879         }
50880         this.isSlid = false;
50881         this.beforeSlide();
50882         this.el.slideOut(this.getSlideAnchor(), {
50883             callback: function(){
50884                 this.el.setLeftTop(-10000, -10000);
50885                 this.afterSlide();
50886                 this.afterSlideIn();
50887                 Roo.callback(cb);
50888             },
50889             scope: this,
50890             block: true
50891         });
50892     },
50893     
50894     slideInIf : function(e){
50895         if(!e.within(this.el)){
50896             this.slideIn();
50897         }
50898     },
50899
50900     animateCollapse : function(){
50901         this.beforeSlide();
50902         this.el.setStyle("z-index", 20000);
50903         var anchor = this.getSlideAnchor();
50904         this.el.slideOut(anchor, {
50905             callback : function(){
50906                 this.el.setStyle("z-index", "");
50907                 this.collapsedEl.slideIn(anchor, {duration:.3});
50908                 this.afterSlide();
50909                 this.el.setLocation(-10000,-10000);
50910                 this.el.hide();
50911                 this.fireEvent("collapsed", this);
50912             },
50913             scope: this,
50914             block: true
50915         });
50916     },
50917
50918     animateExpand : function(){
50919         this.beforeSlide();
50920         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50921         this.el.setStyle("z-index", 20000);
50922         this.collapsedEl.hide({
50923             duration:.1
50924         });
50925         this.el.slideIn(this.getSlideAnchor(), {
50926             callback : function(){
50927                 this.el.setStyle("z-index", "");
50928                 this.afterSlide();
50929                 if(this.split){
50930                     this.split.el.show();
50931                 }
50932                 this.fireEvent("invalidated", this);
50933                 this.fireEvent("expanded", this);
50934             },
50935             scope: this,
50936             block: true
50937         });
50938     },
50939
50940     anchors : {
50941         "west" : "left",
50942         "east" : "right",
50943         "north" : "top",
50944         "south" : "bottom"
50945     },
50946
50947     sanchors : {
50948         "west" : "l",
50949         "east" : "r",
50950         "north" : "t",
50951         "south" : "b"
50952     },
50953
50954     canchors : {
50955         "west" : "tl-tr",
50956         "east" : "tr-tl",
50957         "north" : "tl-bl",
50958         "south" : "bl-tl"
50959     },
50960
50961     getAnchor : function(){
50962         return this.anchors[this.position];
50963     },
50964
50965     getCollapseAnchor : function(){
50966         return this.canchors[this.position];
50967     },
50968
50969     getSlideAnchor : function(){
50970         return this.sanchors[this.position];
50971     },
50972
50973     getAlignAdj : function(){
50974         var cm = this.cmargins;
50975         switch(this.position){
50976             case "west":
50977                 return [0, 0];
50978             break;
50979             case "east":
50980                 return [0, 0];
50981             break;
50982             case "north":
50983                 return [0, 0];
50984             break;
50985             case "south":
50986                 return [0, 0];
50987             break;
50988         }
50989     },
50990
50991     getExpandAdj : function(){
50992         var c = this.collapsedEl, cm = this.cmargins;
50993         switch(this.position){
50994             case "west":
50995                 return [-(cm.right+c.getWidth()+cm.left), 0];
50996             break;
50997             case "east":
50998                 return [cm.right+c.getWidth()+cm.left, 0];
50999             break;
51000             case "north":
51001                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51002             break;
51003             case "south":
51004                 return [0, cm.top+cm.bottom+c.getHeight()];
51005             break;
51006         }
51007     }
51008 });/*
51009  * Based on:
51010  * Ext JS Library 1.1.1
51011  * Copyright(c) 2006-2007, Ext JS, LLC.
51012  *
51013  * Originally Released Under LGPL - original licence link has changed is not relivant.
51014  *
51015  * Fork - LGPL
51016  * <script type="text/javascript">
51017  */
51018 /*
51019  * These classes are private internal classes
51020  */
51021 Roo.CenterLayoutRegion = function(mgr, config){
51022     Roo.LayoutRegion.call(this, mgr, config, "center");
51023     this.visible = true;
51024     this.minWidth = config.minWidth || 20;
51025     this.minHeight = config.minHeight || 20;
51026 };
51027
51028 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51029     hide : function(){
51030         // center panel can't be hidden
51031     },
51032     
51033     show : function(){
51034         // center panel can't be hidden
51035     },
51036     
51037     getMinWidth: function(){
51038         return this.minWidth;
51039     },
51040     
51041     getMinHeight: function(){
51042         return this.minHeight;
51043     }
51044 });
51045
51046
51047 Roo.NorthLayoutRegion = function(mgr, config){
51048     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51049     if(this.split){
51050         this.split.placement = Roo.SplitBar.TOP;
51051         this.split.orientation = Roo.SplitBar.VERTICAL;
51052         this.split.el.addClass("x-layout-split-v");
51053     }
51054     var size = config.initialSize || config.height;
51055     if(typeof size != "undefined"){
51056         this.el.setHeight(size);
51057     }
51058 };
51059 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51060     orientation: Roo.SplitBar.VERTICAL,
51061     getBox : function(){
51062         if(this.collapsed){
51063             return this.collapsedEl.getBox();
51064         }
51065         var box = this.el.getBox();
51066         if(this.split){
51067             box.height += this.split.el.getHeight();
51068         }
51069         return box;
51070     },
51071     
51072     updateBox : function(box){
51073         if(this.split && !this.collapsed){
51074             box.height -= this.split.el.getHeight();
51075             this.split.el.setLeft(box.x);
51076             this.split.el.setTop(box.y+box.height);
51077             this.split.el.setWidth(box.width);
51078         }
51079         if(this.collapsed){
51080             this.updateBody(box.width, null);
51081         }
51082         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51083     }
51084 });
51085
51086 Roo.SouthLayoutRegion = function(mgr, config){
51087     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51088     if(this.split){
51089         this.split.placement = Roo.SplitBar.BOTTOM;
51090         this.split.orientation = Roo.SplitBar.VERTICAL;
51091         this.split.el.addClass("x-layout-split-v");
51092     }
51093     var size = config.initialSize || config.height;
51094     if(typeof size != "undefined"){
51095         this.el.setHeight(size);
51096     }
51097 };
51098 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51099     orientation: Roo.SplitBar.VERTICAL,
51100     getBox : function(){
51101         if(this.collapsed){
51102             return this.collapsedEl.getBox();
51103         }
51104         var box = this.el.getBox();
51105         if(this.split){
51106             var sh = this.split.el.getHeight();
51107             box.height += sh;
51108             box.y -= sh;
51109         }
51110         return box;
51111     },
51112     
51113     updateBox : function(box){
51114         if(this.split && !this.collapsed){
51115             var sh = this.split.el.getHeight();
51116             box.height -= sh;
51117             box.y += sh;
51118             this.split.el.setLeft(box.x);
51119             this.split.el.setTop(box.y-sh);
51120             this.split.el.setWidth(box.width);
51121         }
51122         if(this.collapsed){
51123             this.updateBody(box.width, null);
51124         }
51125         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51126     }
51127 });
51128
51129 Roo.EastLayoutRegion = function(mgr, config){
51130     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51131     if(this.split){
51132         this.split.placement = Roo.SplitBar.RIGHT;
51133         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51134         this.split.el.addClass("x-layout-split-h");
51135     }
51136     var size = config.initialSize || config.width;
51137     if(typeof size != "undefined"){
51138         this.el.setWidth(size);
51139     }
51140 };
51141 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51142     orientation: Roo.SplitBar.HORIZONTAL,
51143     getBox : function(){
51144         if(this.collapsed){
51145             return this.collapsedEl.getBox();
51146         }
51147         var box = this.el.getBox();
51148         if(this.split){
51149             var sw = this.split.el.getWidth();
51150             box.width += sw;
51151             box.x -= sw;
51152         }
51153         return box;
51154     },
51155
51156     updateBox : function(box){
51157         if(this.split && !this.collapsed){
51158             var sw = this.split.el.getWidth();
51159             box.width -= sw;
51160             this.split.el.setLeft(box.x);
51161             this.split.el.setTop(box.y);
51162             this.split.el.setHeight(box.height);
51163             box.x += sw;
51164         }
51165         if(this.collapsed){
51166             this.updateBody(null, box.height);
51167         }
51168         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51169     }
51170 });
51171
51172 Roo.WestLayoutRegion = function(mgr, config){
51173     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51174     if(this.split){
51175         this.split.placement = Roo.SplitBar.LEFT;
51176         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51177         this.split.el.addClass("x-layout-split-h");
51178     }
51179     var size = config.initialSize || config.width;
51180     if(typeof size != "undefined"){
51181         this.el.setWidth(size);
51182     }
51183 };
51184 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51185     orientation: Roo.SplitBar.HORIZONTAL,
51186     getBox : function(){
51187         if(this.collapsed){
51188             return this.collapsedEl.getBox();
51189         }
51190         var box = this.el.getBox();
51191         if(this.split){
51192             box.width += this.split.el.getWidth();
51193         }
51194         return box;
51195     },
51196     
51197     updateBox : function(box){
51198         if(this.split && !this.collapsed){
51199             var sw = this.split.el.getWidth();
51200             box.width -= sw;
51201             this.split.el.setLeft(box.x+box.width);
51202             this.split.el.setTop(box.y);
51203             this.split.el.setHeight(box.height);
51204         }
51205         if(this.collapsed){
51206             this.updateBody(null, box.height);
51207         }
51208         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51209     }
51210 });
51211 /*
51212  * Based on:
51213  * Ext JS Library 1.1.1
51214  * Copyright(c) 2006-2007, Ext JS, LLC.
51215  *
51216  * Originally Released Under LGPL - original licence link has changed is not relivant.
51217  *
51218  * Fork - LGPL
51219  * <script type="text/javascript">
51220  */
51221  
51222  
51223 /*
51224  * Private internal class for reading and applying state
51225  */
51226 Roo.LayoutStateManager = function(layout){
51227      // default empty state
51228      this.state = {
51229         north: {},
51230         south: {},
51231         east: {},
51232         west: {}       
51233     };
51234 };
51235
51236 Roo.LayoutStateManager.prototype = {
51237     init : function(layout, provider){
51238         this.provider = provider;
51239         var state = provider.get(layout.id+"-layout-state");
51240         if(state){
51241             var wasUpdating = layout.isUpdating();
51242             if(!wasUpdating){
51243                 layout.beginUpdate();
51244             }
51245             for(var key in state){
51246                 if(typeof state[key] != "function"){
51247                     var rstate = state[key];
51248                     var r = layout.getRegion(key);
51249                     if(r && rstate){
51250                         if(rstate.size){
51251                             r.resizeTo(rstate.size);
51252                         }
51253                         if(rstate.collapsed == true){
51254                             r.collapse(true);
51255                         }else{
51256                             r.expand(null, true);
51257                         }
51258                     }
51259                 }
51260             }
51261             if(!wasUpdating){
51262                 layout.endUpdate();
51263             }
51264             this.state = state; 
51265         }
51266         this.layout = layout;
51267         layout.on("regionresized", this.onRegionResized, this);
51268         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51269         layout.on("regionexpanded", this.onRegionExpanded, this);
51270     },
51271     
51272     storeState : function(){
51273         this.provider.set(this.layout.id+"-layout-state", this.state);
51274     },
51275     
51276     onRegionResized : function(region, newSize){
51277         this.state[region.getPosition()].size = newSize;
51278         this.storeState();
51279     },
51280     
51281     onRegionCollapsed : function(region){
51282         this.state[region.getPosition()].collapsed = true;
51283         this.storeState();
51284     },
51285     
51286     onRegionExpanded : function(region){
51287         this.state[region.getPosition()].collapsed = false;
51288         this.storeState();
51289     }
51290 };/*
51291  * Based on:
51292  * Ext JS Library 1.1.1
51293  * Copyright(c) 2006-2007, Ext JS, LLC.
51294  *
51295  * Originally Released Under LGPL - original licence link has changed is not relivant.
51296  *
51297  * Fork - LGPL
51298  * <script type="text/javascript">
51299  */
51300 /**
51301  * @class Roo.ContentPanel
51302  * @extends Roo.util.Observable
51303  * A basic ContentPanel element.
51304  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51305  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51306  * @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
51307  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51308  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51309  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51310  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51311  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51312  * @cfg {String} title          The title for this panel
51313  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51314  * @cfg {String} url            Calls {@link #setUrl} with this value
51315  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51316  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51317  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51318  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51319
51320  * @constructor
51321  * Create a new ContentPanel.
51322  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51323  * @param {String/Object} config A string to set only the title or a config object
51324  * @param {String} content (optional) Set the HTML content for this panel
51325  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51326  */
51327 Roo.ContentPanel = function(el, config, content){
51328     
51329      
51330     /*
51331     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51332         config = el;
51333         el = Roo.id();
51334     }
51335     if (config && config.parentLayout) { 
51336         el = config.parentLayout.el.createChild(); 
51337     }
51338     */
51339     if(el.autoCreate){ // xtype is available if this is called from factory
51340         config = el;
51341         el = Roo.id();
51342     }
51343     this.el = Roo.get(el);
51344     if(!this.el && config && config.autoCreate){
51345         if(typeof config.autoCreate == "object"){
51346             if(!config.autoCreate.id){
51347                 config.autoCreate.id = config.id||el;
51348             }
51349             this.el = Roo.DomHelper.append(document.body,
51350                         config.autoCreate, true);
51351         }else{
51352             this.el = Roo.DomHelper.append(document.body,
51353                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51354         }
51355     }
51356     this.closable = false;
51357     this.loaded = false;
51358     this.active = false;
51359     if(typeof config == "string"){
51360         this.title = config;
51361     }else{
51362         Roo.apply(this, config);
51363     }
51364     
51365     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51366         this.wrapEl = this.el.wrap();
51367         this.toolbar.container = this.el.insertSibling(false, 'before');
51368         this.toolbar = new Roo.Toolbar(this.toolbar);
51369     }
51370     
51371     // xtype created footer. - not sure if will work as we normally have to render first..
51372     if (this.footer && !this.footer.el && this.footer.xtype) {
51373         if (!this.wrapEl) {
51374             this.wrapEl = this.el.wrap();
51375         }
51376     
51377         this.footer.container = this.wrapEl.createChild();
51378          
51379         this.footer = Roo.factory(this.footer, Roo);
51380         
51381     }
51382     
51383     if(this.resizeEl){
51384         this.resizeEl = Roo.get(this.resizeEl, true);
51385     }else{
51386         this.resizeEl = this.el;
51387     }
51388     // handle view.xtype
51389     
51390  
51391     
51392     
51393     this.addEvents({
51394         /**
51395          * @event activate
51396          * Fires when this panel is activated. 
51397          * @param {Roo.ContentPanel} this
51398          */
51399         "activate" : true,
51400         /**
51401          * @event deactivate
51402          * Fires when this panel is activated. 
51403          * @param {Roo.ContentPanel} this
51404          */
51405         "deactivate" : true,
51406
51407         /**
51408          * @event resize
51409          * Fires when this panel is resized if fitToFrame is true.
51410          * @param {Roo.ContentPanel} this
51411          * @param {Number} width The width after any component adjustments
51412          * @param {Number} height The height after any component adjustments
51413          */
51414         "resize" : true,
51415         
51416          /**
51417          * @event render
51418          * Fires when this tab is created
51419          * @param {Roo.ContentPanel} this
51420          */
51421         "render" : true
51422         
51423         
51424         
51425     });
51426     
51427
51428     
51429     
51430     if(this.autoScroll){
51431         this.resizeEl.setStyle("overflow", "auto");
51432     } else {
51433         // fix randome scrolling
51434         this.el.on('scroll', function() {
51435             Roo.log('fix random scolling');
51436             this.scrollTo('top',0); 
51437         });
51438     }
51439     content = content || this.content;
51440     if(content){
51441         this.setContent(content);
51442     }
51443     if(config && config.url){
51444         this.setUrl(this.url, this.params, this.loadOnce);
51445     }
51446     
51447     
51448     
51449     Roo.ContentPanel.superclass.constructor.call(this);
51450     
51451     if (this.view && typeof(this.view.xtype) != 'undefined') {
51452         this.view.el = this.el.appendChild(document.createElement("div"));
51453         this.view = Roo.factory(this.view); 
51454         this.view.render  &&  this.view.render(false, '');  
51455     }
51456     
51457     
51458     this.fireEvent('render', this);
51459 };
51460
51461 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51462     tabTip:'',
51463     setRegion : function(region){
51464         this.region = region;
51465         if(region){
51466            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51467         }else{
51468            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51469         } 
51470     },
51471     
51472     /**
51473      * Returns the toolbar for this Panel if one was configured. 
51474      * @return {Roo.Toolbar} 
51475      */
51476     getToolbar : function(){
51477         return this.toolbar;
51478     },
51479     
51480     setActiveState : function(active){
51481         this.active = active;
51482         if(!active){
51483             this.fireEvent("deactivate", this);
51484         }else{
51485             this.fireEvent("activate", this);
51486         }
51487     },
51488     /**
51489      * Updates this panel's element
51490      * @param {String} content The new content
51491      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51492     */
51493     setContent : function(content, loadScripts){
51494         this.el.update(content, loadScripts);
51495     },
51496
51497     ignoreResize : function(w, h){
51498         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51499             return true;
51500         }else{
51501             this.lastSize = {width: w, height: h};
51502             return false;
51503         }
51504     },
51505     /**
51506      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51507      * @return {Roo.UpdateManager} The UpdateManager
51508      */
51509     getUpdateManager : function(){
51510         return this.el.getUpdateManager();
51511     },
51512      /**
51513      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51514      * @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:
51515 <pre><code>
51516 panel.load({
51517     url: "your-url.php",
51518     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51519     callback: yourFunction,
51520     scope: yourObject, //(optional scope)
51521     discardUrl: false,
51522     nocache: false,
51523     text: "Loading...",
51524     timeout: 30,
51525     scripts: false
51526 });
51527 </code></pre>
51528      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51529      * 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.
51530      * @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}
51531      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51532      * @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.
51533      * @return {Roo.ContentPanel} this
51534      */
51535     load : function(){
51536         var um = this.el.getUpdateManager();
51537         um.update.apply(um, arguments);
51538         return this;
51539     },
51540
51541
51542     /**
51543      * 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.
51544      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51545      * @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)
51546      * @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)
51547      * @return {Roo.UpdateManager} The UpdateManager
51548      */
51549     setUrl : function(url, params, loadOnce){
51550         if(this.refreshDelegate){
51551             this.removeListener("activate", this.refreshDelegate);
51552         }
51553         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51554         this.on("activate", this.refreshDelegate);
51555         return this.el.getUpdateManager();
51556     },
51557     
51558     _handleRefresh : function(url, params, loadOnce){
51559         if(!loadOnce || !this.loaded){
51560             var updater = this.el.getUpdateManager();
51561             updater.update(url, params, this._setLoaded.createDelegate(this));
51562         }
51563     },
51564     
51565     _setLoaded : function(){
51566         this.loaded = true;
51567     }, 
51568     
51569     /**
51570      * Returns this panel's id
51571      * @return {String} 
51572      */
51573     getId : function(){
51574         return this.el.id;
51575     },
51576     
51577     /** 
51578      * Returns this panel's element - used by regiosn to add.
51579      * @return {Roo.Element} 
51580      */
51581     getEl : function(){
51582         return this.wrapEl || this.el;
51583     },
51584     
51585     adjustForComponents : function(width, height)
51586     {
51587         //Roo.log('adjustForComponents ');
51588         if(this.resizeEl != this.el){
51589             width -= this.el.getFrameWidth('lr');
51590             height -= this.el.getFrameWidth('tb');
51591         }
51592         if(this.toolbar){
51593             var te = this.toolbar.getEl();
51594             height -= te.getHeight();
51595             te.setWidth(width);
51596         }
51597         if(this.footer){
51598             var te = this.footer.getEl();
51599             Roo.log("footer:" + te.getHeight());
51600             
51601             height -= te.getHeight();
51602             te.setWidth(width);
51603         }
51604         
51605         
51606         if(this.adjustments){
51607             width += this.adjustments[0];
51608             height += this.adjustments[1];
51609         }
51610         return {"width": width, "height": height};
51611     },
51612     
51613     setSize : function(width, height){
51614         if(this.fitToFrame && !this.ignoreResize(width, height)){
51615             if(this.fitContainer && this.resizeEl != this.el){
51616                 this.el.setSize(width, height);
51617             }
51618             var size = this.adjustForComponents(width, height);
51619             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51620             this.fireEvent('resize', this, size.width, size.height);
51621         }
51622     },
51623     
51624     /**
51625      * Returns this panel's title
51626      * @return {String} 
51627      */
51628     getTitle : function(){
51629         return this.title;
51630     },
51631     
51632     /**
51633      * Set this panel's title
51634      * @param {String} title
51635      */
51636     setTitle : function(title){
51637         this.title = title;
51638         if(this.region){
51639             this.region.updatePanelTitle(this, title);
51640         }
51641     },
51642     
51643     /**
51644      * Returns true is this panel was configured to be closable
51645      * @return {Boolean} 
51646      */
51647     isClosable : function(){
51648         return this.closable;
51649     },
51650     
51651     beforeSlide : function(){
51652         this.el.clip();
51653         this.resizeEl.clip();
51654     },
51655     
51656     afterSlide : function(){
51657         this.el.unclip();
51658         this.resizeEl.unclip();
51659     },
51660     
51661     /**
51662      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51663      *   Will fail silently if the {@link #setUrl} method has not been called.
51664      *   This does not activate the panel, just updates its content.
51665      */
51666     refresh : function(){
51667         if(this.refreshDelegate){
51668            this.loaded = false;
51669            this.refreshDelegate();
51670         }
51671     },
51672     
51673     /**
51674      * Destroys this panel
51675      */
51676     destroy : function(){
51677         this.el.removeAllListeners();
51678         var tempEl = document.createElement("span");
51679         tempEl.appendChild(this.el.dom);
51680         tempEl.innerHTML = "";
51681         this.el.remove();
51682         this.el = null;
51683     },
51684     
51685     /**
51686      * form - if the content panel contains a form - this is a reference to it.
51687      * @type {Roo.form.Form}
51688      */
51689     form : false,
51690     /**
51691      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51692      *    This contains a reference to it.
51693      * @type {Roo.View}
51694      */
51695     view : false,
51696     
51697       /**
51698      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51699      * <pre><code>
51700
51701 layout.addxtype({
51702        xtype : 'Form',
51703        items: [ .... ]
51704    }
51705 );
51706
51707 </code></pre>
51708      * @param {Object} cfg Xtype definition of item to add.
51709      */
51710     
51711     addxtype : function(cfg) {
51712         // add form..
51713         if (cfg.xtype.match(/^Form$/)) {
51714             
51715             var el;
51716             //if (this.footer) {
51717             //    el = this.footer.container.insertSibling(false, 'before');
51718             //} else {
51719                 el = this.el.createChild();
51720             //}
51721
51722             this.form = new  Roo.form.Form(cfg);
51723             
51724             
51725             if ( this.form.allItems.length) this.form.render(el.dom);
51726             return this.form;
51727         }
51728         // should only have one of theses..
51729         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51730             // views.. should not be just added - used named prop 'view''
51731             
51732             cfg.el = this.el.appendChild(document.createElement("div"));
51733             // factory?
51734             
51735             var ret = new Roo.factory(cfg);
51736              
51737              ret.render && ret.render(false, ''); // render blank..
51738             this.view = ret;
51739             return ret;
51740         }
51741         return false;
51742     }
51743 });
51744
51745 /**
51746  * @class Roo.GridPanel
51747  * @extends Roo.ContentPanel
51748  * @constructor
51749  * Create a new GridPanel.
51750  * @param {Roo.grid.Grid} grid The grid for this panel
51751  * @param {String/Object} config A string to set only the panel's title, or a config object
51752  */
51753 Roo.GridPanel = function(grid, config){
51754     
51755   
51756     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51757         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51758         
51759     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51760     
51761     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51762     
51763     if(this.toolbar){
51764         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51765     }
51766     // xtype created footer. - not sure if will work as we normally have to render first..
51767     if (this.footer && !this.footer.el && this.footer.xtype) {
51768         
51769         this.footer.container = this.grid.getView().getFooterPanel(true);
51770         this.footer.dataSource = this.grid.dataSource;
51771         this.footer = Roo.factory(this.footer, Roo);
51772         
51773     }
51774     
51775     grid.monitorWindowResize = false; // turn off autosizing
51776     grid.autoHeight = false;
51777     grid.autoWidth = false;
51778     this.grid = grid;
51779     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51780 };
51781
51782 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51783     getId : function(){
51784         return this.grid.id;
51785     },
51786     
51787     /**
51788      * Returns the grid for this panel
51789      * @return {Roo.grid.Grid} 
51790      */
51791     getGrid : function(){
51792         return this.grid;    
51793     },
51794     
51795     setSize : function(width, height){
51796         if(!this.ignoreResize(width, height)){
51797             var grid = this.grid;
51798             var size = this.adjustForComponents(width, height);
51799             grid.getGridEl().setSize(size.width, size.height);
51800             grid.autoSize();
51801         }
51802     },
51803     
51804     beforeSlide : function(){
51805         this.grid.getView().scroller.clip();
51806     },
51807     
51808     afterSlide : function(){
51809         this.grid.getView().scroller.unclip();
51810     },
51811     
51812     destroy : function(){
51813         this.grid.destroy();
51814         delete this.grid;
51815         Roo.GridPanel.superclass.destroy.call(this); 
51816     }
51817 });
51818
51819
51820 /**
51821  * @class Roo.NestedLayoutPanel
51822  * @extends Roo.ContentPanel
51823  * @constructor
51824  * Create a new NestedLayoutPanel.
51825  * 
51826  * 
51827  * @param {Roo.BorderLayout} layout The layout for this panel
51828  * @param {String/Object} config A string to set only the title or a config object
51829  */
51830 Roo.NestedLayoutPanel = function(layout, config)
51831 {
51832     // construct with only one argument..
51833     /* FIXME - implement nicer consturctors
51834     if (layout.layout) {
51835         config = layout;
51836         layout = config.layout;
51837         delete config.layout;
51838     }
51839     if (layout.xtype && !layout.getEl) {
51840         // then layout needs constructing..
51841         layout = Roo.factory(layout, Roo);
51842     }
51843     */
51844     
51845     
51846     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51847     
51848     layout.monitorWindowResize = false; // turn off autosizing
51849     this.layout = layout;
51850     this.layout.getEl().addClass("x-layout-nested-layout");
51851     
51852     
51853     
51854     
51855 };
51856
51857 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51858
51859     setSize : function(width, height){
51860         if(!this.ignoreResize(width, height)){
51861             var size = this.adjustForComponents(width, height);
51862             var el = this.layout.getEl();
51863             el.setSize(size.width, size.height);
51864             var touch = el.dom.offsetWidth;
51865             this.layout.layout();
51866             // ie requires a double layout on the first pass
51867             if(Roo.isIE && !this.initialized){
51868                 this.initialized = true;
51869                 this.layout.layout();
51870             }
51871         }
51872     },
51873     
51874     // activate all subpanels if not currently active..
51875     
51876     setActiveState : function(active){
51877         this.active = active;
51878         if(!active){
51879             this.fireEvent("deactivate", this);
51880             return;
51881         }
51882         
51883         this.fireEvent("activate", this);
51884         // not sure if this should happen before or after..
51885         if (!this.layout) {
51886             return; // should not happen..
51887         }
51888         var reg = false;
51889         for (var r in this.layout.regions) {
51890             reg = this.layout.getRegion(r);
51891             if (reg.getActivePanel()) {
51892                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51893                 reg.setActivePanel(reg.getActivePanel());
51894                 continue;
51895             }
51896             if (!reg.panels.length) {
51897                 continue;
51898             }
51899             reg.showPanel(reg.getPanel(0));
51900         }
51901         
51902         
51903         
51904         
51905     },
51906     
51907     /**
51908      * Returns the nested BorderLayout for this panel
51909      * @return {Roo.BorderLayout} 
51910      */
51911     getLayout : function(){
51912         return this.layout;
51913     },
51914     
51915      /**
51916      * Adds a xtype elements to the layout of the nested panel
51917      * <pre><code>
51918
51919 panel.addxtype({
51920        xtype : 'ContentPanel',
51921        region: 'west',
51922        items: [ .... ]
51923    }
51924 );
51925
51926 panel.addxtype({
51927         xtype : 'NestedLayoutPanel',
51928         region: 'west',
51929         layout: {
51930            center: { },
51931            west: { }   
51932         },
51933         items : [ ... list of content panels or nested layout panels.. ]
51934    }
51935 );
51936 </code></pre>
51937      * @param {Object} cfg Xtype definition of item to add.
51938      */
51939     addxtype : function(cfg) {
51940         return this.layout.addxtype(cfg);
51941     
51942     }
51943 });
51944
51945 Roo.ScrollPanel = function(el, config, content){
51946     config = config || {};
51947     config.fitToFrame = true;
51948     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51949     
51950     this.el.dom.style.overflow = "hidden";
51951     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51952     this.el.removeClass("x-layout-inactive-content");
51953     this.el.on("mousewheel", this.onWheel, this);
51954
51955     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51956     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51957     up.unselectable(); down.unselectable();
51958     up.on("click", this.scrollUp, this);
51959     down.on("click", this.scrollDown, this);
51960     up.addClassOnOver("x-scroller-btn-over");
51961     down.addClassOnOver("x-scroller-btn-over");
51962     up.addClassOnClick("x-scroller-btn-click");
51963     down.addClassOnClick("x-scroller-btn-click");
51964     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51965
51966     this.resizeEl = this.el;
51967     this.el = wrap; this.up = up; this.down = down;
51968 };
51969
51970 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51971     increment : 100,
51972     wheelIncrement : 5,
51973     scrollUp : function(){
51974         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51975     },
51976
51977     scrollDown : function(){
51978         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51979     },
51980
51981     afterScroll : function(){
51982         var el = this.resizeEl;
51983         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51984         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51985         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51986     },
51987
51988     setSize : function(){
51989         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
51990         this.afterScroll();
51991     },
51992
51993     onWheel : function(e){
51994         var d = e.getWheelDelta();
51995         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
51996         this.afterScroll();
51997         e.stopEvent();
51998     },
51999
52000     setContent : function(content, loadScripts){
52001         this.resizeEl.update(content, loadScripts);
52002     }
52003
52004 });
52005
52006
52007
52008
52009
52010
52011
52012
52013
52014 /**
52015  * @class Roo.TreePanel
52016  * @extends Roo.ContentPanel
52017  * @constructor
52018  * Create a new TreePanel. - defaults to fit/scoll contents.
52019  * @param {String/Object} config A string to set only the panel's title, or a config object
52020  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52021  */
52022 Roo.TreePanel = function(config){
52023     var el = config.el;
52024     var tree = config.tree;
52025     delete config.tree; 
52026     delete config.el; // hopefull!
52027     
52028     // wrapper for IE7 strict & safari scroll issue
52029     
52030     var treeEl = el.createChild();
52031     config.resizeEl = treeEl;
52032     
52033     
52034     
52035     Roo.TreePanel.superclass.constructor.call(this, el, config);
52036  
52037  
52038     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52039     //console.log(tree);
52040     this.on('activate', function()
52041     {
52042         if (this.tree.rendered) {
52043             return;
52044         }
52045         //console.log('render tree');
52046         this.tree.render();
52047     });
52048     // this should not be needed.. - it's actually the 'el' that resizes?
52049     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52050     
52051     //this.on('resize',  function (cp, w, h) {
52052     //        this.tree.innerCt.setWidth(w);
52053     //        this.tree.innerCt.setHeight(h);
52054     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52055     //});
52056
52057         
52058     
52059 };
52060
52061 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52062     fitToFrame : true,
52063     autoScroll : true
52064 });
52065
52066
52067
52068
52069
52070
52071
52072
52073
52074
52075
52076 /*
52077  * Based on:
52078  * Ext JS Library 1.1.1
52079  * Copyright(c) 2006-2007, Ext JS, LLC.
52080  *
52081  * Originally Released Under LGPL - original licence link has changed is not relivant.
52082  *
52083  * Fork - LGPL
52084  * <script type="text/javascript">
52085  */
52086  
52087
52088 /**
52089  * @class Roo.ReaderLayout
52090  * @extends Roo.BorderLayout
52091  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52092  * center region containing two nested regions (a top one for a list view and one for item preview below),
52093  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52094  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52095  * expedites the setup of the overall layout and regions for this common application style.
52096  * Example:
52097  <pre><code>
52098 var reader = new Roo.ReaderLayout();
52099 var CP = Roo.ContentPanel;  // shortcut for adding
52100
52101 reader.beginUpdate();
52102 reader.add("north", new CP("north", "North"));
52103 reader.add("west", new CP("west", {title: "West"}));
52104 reader.add("east", new CP("east", {title: "East"}));
52105
52106 reader.regions.listView.add(new CP("listView", "List"));
52107 reader.regions.preview.add(new CP("preview", "Preview"));
52108 reader.endUpdate();
52109 </code></pre>
52110 * @constructor
52111 * Create a new ReaderLayout
52112 * @param {Object} config Configuration options
52113 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52114 * document.body if omitted)
52115 */
52116 Roo.ReaderLayout = function(config, renderTo){
52117     var c = config || {size:{}};
52118     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52119         north: c.north !== false ? Roo.apply({
52120             split:false,
52121             initialSize: 32,
52122             titlebar: false
52123         }, c.north) : false,
52124         west: c.west !== false ? Roo.apply({
52125             split:true,
52126             initialSize: 200,
52127             minSize: 175,
52128             maxSize: 400,
52129             titlebar: true,
52130             collapsible: true,
52131             animate: true,
52132             margins:{left:5,right:0,bottom:5,top:5},
52133             cmargins:{left:5,right:5,bottom:5,top:5}
52134         }, c.west) : false,
52135         east: c.east !== false ? Roo.apply({
52136             split:true,
52137             initialSize: 200,
52138             minSize: 175,
52139             maxSize: 400,
52140             titlebar: true,
52141             collapsible: true,
52142             animate: true,
52143             margins:{left:0,right:5,bottom:5,top:5},
52144             cmargins:{left:5,right:5,bottom:5,top:5}
52145         }, c.east) : false,
52146         center: Roo.apply({
52147             tabPosition: 'top',
52148             autoScroll:false,
52149             closeOnTab: true,
52150             titlebar:false,
52151             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52152         }, c.center)
52153     });
52154
52155     this.el.addClass('x-reader');
52156
52157     this.beginUpdate();
52158
52159     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52160         south: c.preview !== false ? Roo.apply({
52161             split:true,
52162             initialSize: 200,
52163             minSize: 100,
52164             autoScroll:true,
52165             collapsible:true,
52166             titlebar: true,
52167             cmargins:{top:5,left:0, right:0, bottom:0}
52168         }, c.preview) : false,
52169         center: Roo.apply({
52170             autoScroll:false,
52171             titlebar:false,
52172             minHeight:200
52173         }, c.listView)
52174     });
52175     this.add('center', new Roo.NestedLayoutPanel(inner,
52176             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52177
52178     this.endUpdate();
52179
52180     this.regions.preview = inner.getRegion('south');
52181     this.regions.listView = inner.getRegion('center');
52182 };
52183
52184 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52185  * Based on:
52186  * Ext JS Library 1.1.1
52187  * Copyright(c) 2006-2007, Ext JS, LLC.
52188  *
52189  * Originally Released Under LGPL - original licence link has changed is not relivant.
52190  *
52191  * Fork - LGPL
52192  * <script type="text/javascript">
52193  */
52194  
52195 /**
52196  * @class Roo.grid.Grid
52197  * @extends Roo.util.Observable
52198  * This class represents the primary interface of a component based grid control.
52199  * <br><br>Usage:<pre><code>
52200  var grid = new Roo.grid.Grid("my-container-id", {
52201      ds: myDataStore,
52202      cm: myColModel,
52203      selModel: mySelectionModel,
52204      autoSizeColumns: true,
52205      monitorWindowResize: false,
52206      trackMouseOver: true
52207  });
52208  // set any options
52209  grid.render();
52210  * </code></pre>
52211  * <b>Common Problems:</b><br/>
52212  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52213  * element will correct this<br/>
52214  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52215  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52216  * are unpredictable.<br/>
52217  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52218  * grid to calculate dimensions/offsets.<br/>
52219   * @constructor
52220  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52221  * The container MUST have some type of size defined for the grid to fill. The container will be
52222  * automatically set to position relative if it isn't already.
52223  * @param {Object} config A config object that sets properties on this grid.
52224  */
52225 Roo.grid.Grid = function(container, config){
52226         // initialize the container
52227         this.container = Roo.get(container);
52228         this.container.update("");
52229         this.container.setStyle("overflow", "hidden");
52230     this.container.addClass('x-grid-container');
52231
52232     this.id = this.container.id;
52233
52234     Roo.apply(this, config);
52235     // check and correct shorthanded configs
52236     if(this.ds){
52237         this.dataSource = this.ds;
52238         delete this.ds;
52239     }
52240     if(this.cm){
52241         this.colModel = this.cm;
52242         delete this.cm;
52243     }
52244     if(this.sm){
52245         this.selModel = this.sm;
52246         delete this.sm;
52247     }
52248
52249     if (this.selModel) {
52250         this.selModel = Roo.factory(this.selModel, Roo.grid);
52251         this.sm = this.selModel;
52252         this.sm.xmodule = this.xmodule || false;
52253     }
52254     if (typeof(this.colModel.config) == 'undefined') {
52255         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52256         this.cm = this.colModel;
52257         this.cm.xmodule = this.xmodule || false;
52258     }
52259     if (this.dataSource) {
52260         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52261         this.ds = this.dataSource;
52262         this.ds.xmodule = this.xmodule || false;
52263          
52264     }
52265     
52266     
52267     
52268     if(this.width){
52269         this.container.setWidth(this.width);
52270     }
52271
52272     if(this.height){
52273         this.container.setHeight(this.height);
52274     }
52275     /** @private */
52276         this.addEvents({
52277         // raw events
52278         /**
52279          * @event click
52280          * The raw click event for the entire grid.
52281          * @param {Roo.EventObject} e
52282          */
52283         "click" : true,
52284         /**
52285          * @event dblclick
52286          * The raw dblclick event for the entire grid.
52287          * @param {Roo.EventObject} e
52288          */
52289         "dblclick" : true,
52290         /**
52291          * @event contextmenu
52292          * The raw contextmenu event for the entire grid.
52293          * @param {Roo.EventObject} e
52294          */
52295         "contextmenu" : true,
52296         /**
52297          * @event mousedown
52298          * The raw mousedown event for the entire grid.
52299          * @param {Roo.EventObject} e
52300          */
52301         "mousedown" : true,
52302         /**
52303          * @event mouseup
52304          * The raw mouseup event for the entire grid.
52305          * @param {Roo.EventObject} e
52306          */
52307         "mouseup" : true,
52308         /**
52309          * @event mouseover
52310          * The raw mouseover event for the entire grid.
52311          * @param {Roo.EventObject} e
52312          */
52313         "mouseover" : true,
52314         /**
52315          * @event mouseout
52316          * The raw mouseout event for the entire grid.
52317          * @param {Roo.EventObject} e
52318          */
52319         "mouseout" : true,
52320         /**
52321          * @event keypress
52322          * The raw keypress event for the entire grid.
52323          * @param {Roo.EventObject} e
52324          */
52325         "keypress" : true,
52326         /**
52327          * @event keydown
52328          * The raw keydown event for the entire grid.
52329          * @param {Roo.EventObject} e
52330          */
52331         "keydown" : true,
52332
52333         // custom events
52334
52335         /**
52336          * @event cellclick
52337          * Fires when a cell is clicked
52338          * @param {Grid} this
52339          * @param {Number} rowIndex
52340          * @param {Number} columnIndex
52341          * @param {Roo.EventObject} e
52342          */
52343         "cellclick" : true,
52344         /**
52345          * @event celldblclick
52346          * Fires when a cell is double clicked
52347          * @param {Grid} this
52348          * @param {Number} rowIndex
52349          * @param {Number} columnIndex
52350          * @param {Roo.EventObject} e
52351          */
52352         "celldblclick" : true,
52353         /**
52354          * @event rowclick
52355          * Fires when a row is clicked
52356          * @param {Grid} this
52357          * @param {Number} rowIndex
52358          * @param {Roo.EventObject} e
52359          */
52360         "rowclick" : true,
52361         /**
52362          * @event rowdblclick
52363          * Fires when a row is double clicked
52364          * @param {Grid} this
52365          * @param {Number} rowIndex
52366          * @param {Roo.EventObject} e
52367          */
52368         "rowdblclick" : true,
52369         /**
52370          * @event headerclick
52371          * Fires when a header is clicked
52372          * @param {Grid} this
52373          * @param {Number} columnIndex
52374          * @param {Roo.EventObject} e
52375          */
52376         "headerclick" : true,
52377         /**
52378          * @event headerdblclick
52379          * Fires when a header cell is double clicked
52380          * @param {Grid} this
52381          * @param {Number} columnIndex
52382          * @param {Roo.EventObject} e
52383          */
52384         "headerdblclick" : true,
52385         /**
52386          * @event rowcontextmenu
52387          * Fires when a row is right clicked
52388          * @param {Grid} this
52389          * @param {Number} rowIndex
52390          * @param {Roo.EventObject} e
52391          */
52392         "rowcontextmenu" : true,
52393         /**
52394          * @event cellcontextmenu
52395          * Fires when a cell is right clicked
52396          * @param {Grid} this
52397          * @param {Number} rowIndex
52398          * @param {Number} cellIndex
52399          * @param {Roo.EventObject} e
52400          */
52401          "cellcontextmenu" : true,
52402         /**
52403          * @event headercontextmenu
52404          * Fires when a header is right clicked
52405          * @param {Grid} this
52406          * @param {Number} columnIndex
52407          * @param {Roo.EventObject} e
52408          */
52409         "headercontextmenu" : true,
52410         /**
52411          * @event bodyscroll
52412          * Fires when the body element is scrolled
52413          * @param {Number} scrollLeft
52414          * @param {Number} scrollTop
52415          */
52416         "bodyscroll" : true,
52417         /**
52418          * @event columnresize
52419          * Fires when the user resizes a column
52420          * @param {Number} columnIndex
52421          * @param {Number} newSize
52422          */
52423         "columnresize" : true,
52424         /**
52425          * @event columnmove
52426          * Fires when the user moves a column
52427          * @param {Number} oldIndex
52428          * @param {Number} newIndex
52429          */
52430         "columnmove" : true,
52431         /**
52432          * @event startdrag
52433          * Fires when row(s) start being dragged
52434          * @param {Grid} this
52435          * @param {Roo.GridDD} dd The drag drop object
52436          * @param {event} e The raw browser event
52437          */
52438         "startdrag" : true,
52439         /**
52440          * @event enddrag
52441          * Fires when a drag operation is complete
52442          * @param {Grid} this
52443          * @param {Roo.GridDD} dd The drag drop object
52444          * @param {event} e The raw browser event
52445          */
52446         "enddrag" : true,
52447         /**
52448          * @event dragdrop
52449          * Fires when dragged row(s) are dropped on a valid DD target
52450          * @param {Grid} this
52451          * @param {Roo.GridDD} dd The drag drop object
52452          * @param {String} targetId The target drag drop object
52453          * @param {event} e The raw browser event
52454          */
52455         "dragdrop" : true,
52456         /**
52457          * @event dragover
52458          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52459          * @param {Grid} this
52460          * @param {Roo.GridDD} dd The drag drop object
52461          * @param {String} targetId The target drag drop object
52462          * @param {event} e The raw browser event
52463          */
52464         "dragover" : true,
52465         /**
52466          * @event dragenter
52467          *  Fires when the dragged row(s) first cross another DD target while being dragged
52468          * @param {Grid} this
52469          * @param {Roo.GridDD} dd The drag drop object
52470          * @param {String} targetId The target drag drop object
52471          * @param {event} e The raw browser event
52472          */
52473         "dragenter" : true,
52474         /**
52475          * @event dragout
52476          * Fires when the dragged row(s) leave another DD target while being dragged
52477          * @param {Grid} this
52478          * @param {Roo.GridDD} dd The drag drop object
52479          * @param {String} targetId The target drag drop object
52480          * @param {event} e The raw browser event
52481          */
52482         "dragout" : true,
52483         /**
52484          * @event rowclass
52485          * Fires when a row is rendered, so you can change add a style to it.
52486          * @param {GridView} gridview   The grid view
52487          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52488          */
52489         'rowclass' : true,
52490
52491         /**
52492          * @event render
52493          * Fires when the grid is rendered
52494          * @param {Grid} grid
52495          */
52496         'render' : true
52497     });
52498
52499     Roo.grid.Grid.superclass.constructor.call(this);
52500 };
52501 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52502     
52503     /**
52504      * @cfg {String} ddGroup - drag drop group.
52505      */
52506
52507     /**
52508      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52509      */
52510     minColumnWidth : 25,
52511
52512     /**
52513      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52514      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52515      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52516      */
52517     autoSizeColumns : false,
52518
52519     /**
52520      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52521      */
52522     autoSizeHeaders : true,
52523
52524     /**
52525      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52526      */
52527     monitorWindowResize : true,
52528
52529     /**
52530      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52531      * rows measured to get a columns size. Default is 0 (all rows).
52532      */
52533     maxRowsToMeasure : 0,
52534
52535     /**
52536      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52537      */
52538     trackMouseOver : true,
52539
52540     /**
52541     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52542     */
52543     
52544     /**
52545     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52546     */
52547     enableDragDrop : false,
52548     
52549     /**
52550     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52551     */
52552     enableColumnMove : true,
52553     
52554     /**
52555     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52556     */
52557     enableColumnHide : true,
52558     
52559     /**
52560     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52561     */
52562     enableRowHeightSync : false,
52563     
52564     /**
52565     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52566     */
52567     stripeRows : true,
52568     
52569     /**
52570     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52571     */
52572     autoHeight : false,
52573
52574     /**
52575      * @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.
52576      */
52577     autoExpandColumn : false,
52578
52579     /**
52580     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52581     * Default is 50.
52582     */
52583     autoExpandMin : 50,
52584
52585     /**
52586     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52587     */
52588     autoExpandMax : 1000,
52589
52590     /**
52591     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52592     */
52593     view : null,
52594
52595     /**
52596     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52597     */
52598     loadMask : false,
52599     /**
52600     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52601     */
52602     dropTarget: false,
52603     
52604    
52605     
52606     // private
52607     rendered : false,
52608
52609     /**
52610     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52611     * of a fixed width. Default is false.
52612     */
52613     /**
52614     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52615     */
52616     /**
52617      * Called once after all setup has been completed and the grid is ready to be rendered.
52618      * @return {Roo.grid.Grid} this
52619      */
52620     render : function()
52621     {
52622         var c = this.container;
52623         // try to detect autoHeight/width mode
52624         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52625             this.autoHeight = true;
52626         }
52627         var view = this.getView();
52628         view.init(this);
52629
52630         c.on("click", this.onClick, this);
52631         c.on("dblclick", this.onDblClick, this);
52632         c.on("contextmenu", this.onContextMenu, this);
52633         c.on("keydown", this.onKeyDown, this);
52634         if (Roo.isTouch) {
52635             c.on("touchstart", this.onTouchStart, this);
52636         }
52637
52638         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52639
52640         this.getSelectionModel().init(this);
52641
52642         view.render();
52643
52644         if(this.loadMask){
52645             this.loadMask = new Roo.LoadMask(this.container,
52646                     Roo.apply({store:this.dataSource}, this.loadMask));
52647         }
52648         
52649         
52650         if (this.toolbar && this.toolbar.xtype) {
52651             this.toolbar.container = this.getView().getHeaderPanel(true);
52652             this.toolbar = new Roo.Toolbar(this.toolbar);
52653         }
52654         if (this.footer && this.footer.xtype) {
52655             this.footer.dataSource = this.getDataSource();
52656             this.footer.container = this.getView().getFooterPanel(true);
52657             this.footer = Roo.factory(this.footer, Roo);
52658         }
52659         if (this.dropTarget && this.dropTarget.xtype) {
52660             delete this.dropTarget.xtype;
52661             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52662         }
52663         
52664         
52665         this.rendered = true;
52666         this.fireEvent('render', this);
52667         return this;
52668     },
52669
52670         /**
52671          * Reconfigures the grid to use a different Store and Column Model.
52672          * The View will be bound to the new objects and refreshed.
52673          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52674          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52675          */
52676     reconfigure : function(dataSource, colModel){
52677         if(this.loadMask){
52678             this.loadMask.destroy();
52679             this.loadMask = new Roo.LoadMask(this.container,
52680                     Roo.apply({store:dataSource}, this.loadMask));
52681         }
52682         this.view.bind(dataSource, colModel);
52683         this.dataSource = dataSource;
52684         this.colModel = colModel;
52685         this.view.refresh(true);
52686     },
52687
52688     // private
52689     onKeyDown : function(e){
52690         this.fireEvent("keydown", e);
52691     },
52692
52693     /**
52694      * Destroy this grid.
52695      * @param {Boolean} removeEl True to remove the element
52696      */
52697     destroy : function(removeEl, keepListeners){
52698         if(this.loadMask){
52699             this.loadMask.destroy();
52700         }
52701         var c = this.container;
52702         c.removeAllListeners();
52703         this.view.destroy();
52704         this.colModel.purgeListeners();
52705         if(!keepListeners){
52706             this.purgeListeners();
52707         }
52708         c.update("");
52709         if(removeEl === true){
52710             c.remove();
52711         }
52712     },
52713
52714     // private
52715     processEvent : function(name, e){
52716         // does this fire select???
52717         //Roo.log('grid:processEvent '  + name);
52718         
52719         if (name != 'touchstart' ) {
52720             this.fireEvent(name, e);    
52721         }
52722         
52723         var t = e.getTarget();
52724         var v = this.view;
52725         var header = v.findHeaderIndex(t);
52726         if(header !== false){
52727             var ename = name == 'touchstart' ? 'click' : name;
52728              
52729             this.fireEvent("header" + ename, this, header, e);
52730         }else{
52731             var row = v.findRowIndex(t);
52732             var cell = v.findCellIndex(t);
52733             if (name == 'touchstart') {
52734                 // first touch is always a click.
52735                 // hopefull this happens after selection is updated.?
52736                 name = false;
52737                 
52738                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52739                     var cs = this.selModel.getSelectedCell();
52740                     if (row == cs[0] && cell == cs[1]){
52741                         name = 'dblclick';
52742                     }
52743                 }
52744                 if (typeof(this.selModel.getSelections) != 'undefined') {
52745                     var cs = this.selModel.getSelections();
52746                     var ds = this.dataSource;
52747                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52748                         name = 'dblclick';
52749                     }
52750                 }
52751                 if (!name) {
52752                     return;
52753                 }
52754             }
52755             
52756             
52757             if(row !== false){
52758                 this.fireEvent("row" + name, this, row, e);
52759                 if(cell !== false){
52760                     this.fireEvent("cell" + name, this, row, cell, e);
52761                 }
52762             }
52763         }
52764     },
52765
52766     // private
52767     onClick : function(e){
52768         this.processEvent("click", e);
52769     },
52770    // private
52771     onTouchStart : function(e){
52772         this.processEvent("touchstart", e);
52773     },
52774
52775     // private
52776     onContextMenu : function(e, t){
52777         this.processEvent("contextmenu", e);
52778     },
52779
52780     // private
52781     onDblClick : function(e){
52782         this.processEvent("dblclick", e);
52783     },
52784
52785     // private
52786     walkCells : function(row, col, step, fn, scope){
52787         var cm = this.colModel, clen = cm.getColumnCount();
52788         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52789         if(step < 0){
52790             if(col < 0){
52791                 row--;
52792                 first = false;
52793             }
52794             while(row >= 0){
52795                 if(!first){
52796                     col = clen-1;
52797                 }
52798                 first = false;
52799                 while(col >= 0){
52800                     if(fn.call(scope || this, row, col, cm) === true){
52801                         return [row, col];
52802                     }
52803                     col--;
52804                 }
52805                 row--;
52806             }
52807         } else {
52808             if(col >= clen){
52809                 row++;
52810                 first = false;
52811             }
52812             while(row < rlen){
52813                 if(!first){
52814                     col = 0;
52815                 }
52816                 first = false;
52817                 while(col < clen){
52818                     if(fn.call(scope || this, row, col, cm) === true){
52819                         return [row, col];
52820                     }
52821                     col++;
52822                 }
52823                 row++;
52824             }
52825         }
52826         return null;
52827     },
52828
52829     // private
52830     getSelections : function(){
52831         return this.selModel.getSelections();
52832     },
52833
52834     /**
52835      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52836      * but if manual update is required this method will initiate it.
52837      */
52838     autoSize : function(){
52839         if(this.rendered){
52840             this.view.layout();
52841             if(this.view.adjustForScroll){
52842                 this.view.adjustForScroll();
52843             }
52844         }
52845     },
52846
52847     /**
52848      * Returns the grid's underlying element.
52849      * @return {Element} The element
52850      */
52851     getGridEl : function(){
52852         return this.container;
52853     },
52854
52855     // private for compatibility, overridden by editor grid
52856     stopEditing : function(){},
52857
52858     /**
52859      * Returns the grid's SelectionModel.
52860      * @return {SelectionModel}
52861      */
52862     getSelectionModel : function(){
52863         if(!this.selModel){
52864             this.selModel = new Roo.grid.RowSelectionModel();
52865         }
52866         return this.selModel;
52867     },
52868
52869     /**
52870      * Returns the grid's DataSource.
52871      * @return {DataSource}
52872      */
52873     getDataSource : function(){
52874         return this.dataSource;
52875     },
52876
52877     /**
52878      * Returns the grid's ColumnModel.
52879      * @return {ColumnModel}
52880      */
52881     getColumnModel : function(){
52882         return this.colModel;
52883     },
52884
52885     /**
52886      * Returns the grid's GridView object.
52887      * @return {GridView}
52888      */
52889     getView : function(){
52890         if(!this.view){
52891             this.view = new Roo.grid.GridView(this.viewConfig);
52892         }
52893         return this.view;
52894     },
52895     /**
52896      * Called to get grid's drag proxy text, by default returns this.ddText.
52897      * @return {String}
52898      */
52899     getDragDropText : function(){
52900         var count = this.selModel.getCount();
52901         return String.format(this.ddText, count, count == 1 ? '' : 's');
52902     }
52903 });
52904 /**
52905  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52906  * %0 is replaced with the number of selected rows.
52907  * @type String
52908  */
52909 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52910  * Based on:
52911  * Ext JS Library 1.1.1
52912  * Copyright(c) 2006-2007, Ext JS, LLC.
52913  *
52914  * Originally Released Under LGPL - original licence link has changed is not relivant.
52915  *
52916  * Fork - LGPL
52917  * <script type="text/javascript">
52918  */
52919  
52920 Roo.grid.AbstractGridView = function(){
52921         this.grid = null;
52922         
52923         this.events = {
52924             "beforerowremoved" : true,
52925             "beforerowsinserted" : true,
52926             "beforerefresh" : true,
52927             "rowremoved" : true,
52928             "rowsinserted" : true,
52929             "rowupdated" : true,
52930             "refresh" : true
52931         };
52932     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52933 };
52934
52935 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52936     rowClass : "x-grid-row",
52937     cellClass : "x-grid-cell",
52938     tdClass : "x-grid-td",
52939     hdClass : "x-grid-hd",
52940     splitClass : "x-grid-hd-split",
52941     
52942     init: function(grid){
52943         this.grid = grid;
52944                 var cid = this.grid.getGridEl().id;
52945         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52946         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52947         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52948         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52949         },
52950         
52951     getColumnRenderers : function(){
52952         var renderers = [];
52953         var cm = this.grid.colModel;
52954         var colCount = cm.getColumnCount();
52955         for(var i = 0; i < colCount; i++){
52956             renderers[i] = cm.getRenderer(i);
52957         }
52958         return renderers;
52959     },
52960     
52961     getColumnIds : function(){
52962         var ids = [];
52963         var cm = this.grid.colModel;
52964         var colCount = cm.getColumnCount();
52965         for(var i = 0; i < colCount; i++){
52966             ids[i] = cm.getColumnId(i);
52967         }
52968         return ids;
52969     },
52970     
52971     getDataIndexes : function(){
52972         if(!this.indexMap){
52973             this.indexMap = this.buildIndexMap();
52974         }
52975         return this.indexMap.colToData;
52976     },
52977     
52978     getColumnIndexByDataIndex : function(dataIndex){
52979         if(!this.indexMap){
52980             this.indexMap = this.buildIndexMap();
52981         }
52982         return this.indexMap.dataToCol[dataIndex];
52983     },
52984     
52985     /**
52986      * Set a css style for a column dynamically. 
52987      * @param {Number} colIndex The index of the column
52988      * @param {String} name The css property name
52989      * @param {String} value The css value
52990      */
52991     setCSSStyle : function(colIndex, name, value){
52992         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
52993         Roo.util.CSS.updateRule(selector, name, value);
52994     },
52995     
52996     generateRules : function(cm){
52997         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
52998         Roo.util.CSS.removeStyleSheet(rulesId);
52999         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53000             var cid = cm.getColumnId(i);
53001             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53002                          this.tdSelector, cid, " {\n}\n",
53003                          this.hdSelector, cid, " {\n}\n",
53004                          this.splitSelector, cid, " {\n}\n");
53005         }
53006         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53007     }
53008 });/*
53009  * Based on:
53010  * Ext JS Library 1.1.1
53011  * Copyright(c) 2006-2007, Ext JS, LLC.
53012  *
53013  * Originally Released Under LGPL - original licence link has changed is not relivant.
53014  *
53015  * Fork - LGPL
53016  * <script type="text/javascript">
53017  */
53018
53019 // private
53020 // This is a support class used internally by the Grid components
53021 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53022     this.grid = grid;
53023     this.view = grid.getView();
53024     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53025     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53026     if(hd2){
53027         this.setHandleElId(Roo.id(hd));
53028         this.setOuterHandleElId(Roo.id(hd2));
53029     }
53030     this.scroll = false;
53031 };
53032 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53033     maxDragWidth: 120,
53034     getDragData : function(e){
53035         var t = Roo.lib.Event.getTarget(e);
53036         var h = this.view.findHeaderCell(t);
53037         if(h){
53038             return {ddel: h.firstChild, header:h};
53039         }
53040         return false;
53041     },
53042
53043     onInitDrag : function(e){
53044         this.view.headersDisabled = true;
53045         var clone = this.dragData.ddel.cloneNode(true);
53046         clone.id = Roo.id();
53047         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53048         this.proxy.update(clone);
53049         return true;
53050     },
53051
53052     afterValidDrop : function(){
53053         var v = this.view;
53054         setTimeout(function(){
53055             v.headersDisabled = false;
53056         }, 50);
53057     },
53058
53059     afterInvalidDrop : function(){
53060         var v = this.view;
53061         setTimeout(function(){
53062             v.headersDisabled = false;
53063         }, 50);
53064     }
53065 });
53066 /*
53067  * Based on:
53068  * Ext JS Library 1.1.1
53069  * Copyright(c) 2006-2007, Ext JS, LLC.
53070  *
53071  * Originally Released Under LGPL - original licence link has changed is not relivant.
53072  *
53073  * Fork - LGPL
53074  * <script type="text/javascript">
53075  */
53076 // private
53077 // This is a support class used internally by the Grid components
53078 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53079     this.grid = grid;
53080     this.view = grid.getView();
53081     // split the proxies so they don't interfere with mouse events
53082     this.proxyTop = Roo.DomHelper.append(document.body, {
53083         cls:"col-move-top", html:"&#160;"
53084     }, true);
53085     this.proxyBottom = Roo.DomHelper.append(document.body, {
53086         cls:"col-move-bottom", html:"&#160;"
53087     }, true);
53088     this.proxyTop.hide = this.proxyBottom.hide = function(){
53089         this.setLeftTop(-100,-100);
53090         this.setStyle("visibility", "hidden");
53091     };
53092     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53093     // temporarily disabled
53094     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53095     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53096 };
53097 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53098     proxyOffsets : [-4, -9],
53099     fly: Roo.Element.fly,
53100
53101     getTargetFromEvent : function(e){
53102         var t = Roo.lib.Event.getTarget(e);
53103         var cindex = this.view.findCellIndex(t);
53104         if(cindex !== false){
53105             return this.view.getHeaderCell(cindex);
53106         }
53107         return null;
53108     },
53109
53110     nextVisible : function(h){
53111         var v = this.view, cm = this.grid.colModel;
53112         h = h.nextSibling;
53113         while(h){
53114             if(!cm.isHidden(v.getCellIndex(h))){
53115                 return h;
53116             }
53117             h = h.nextSibling;
53118         }
53119         return null;
53120     },
53121
53122     prevVisible : function(h){
53123         var v = this.view, cm = this.grid.colModel;
53124         h = h.prevSibling;
53125         while(h){
53126             if(!cm.isHidden(v.getCellIndex(h))){
53127                 return h;
53128             }
53129             h = h.prevSibling;
53130         }
53131         return null;
53132     },
53133
53134     positionIndicator : function(h, n, e){
53135         var x = Roo.lib.Event.getPageX(e);
53136         var r = Roo.lib.Dom.getRegion(n.firstChild);
53137         var px, pt, py = r.top + this.proxyOffsets[1];
53138         if((r.right - x) <= (r.right-r.left)/2){
53139             px = r.right+this.view.borderWidth;
53140             pt = "after";
53141         }else{
53142             px = r.left;
53143             pt = "before";
53144         }
53145         var oldIndex = this.view.getCellIndex(h);
53146         var newIndex = this.view.getCellIndex(n);
53147
53148         if(this.grid.colModel.isFixed(newIndex)){
53149             return false;
53150         }
53151
53152         var locked = this.grid.colModel.isLocked(newIndex);
53153
53154         if(pt == "after"){
53155             newIndex++;
53156         }
53157         if(oldIndex < newIndex){
53158             newIndex--;
53159         }
53160         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53161             return false;
53162         }
53163         px +=  this.proxyOffsets[0];
53164         this.proxyTop.setLeftTop(px, py);
53165         this.proxyTop.show();
53166         if(!this.bottomOffset){
53167             this.bottomOffset = this.view.mainHd.getHeight();
53168         }
53169         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53170         this.proxyBottom.show();
53171         return pt;
53172     },
53173
53174     onNodeEnter : function(n, dd, e, data){
53175         if(data.header != n){
53176             this.positionIndicator(data.header, n, e);
53177         }
53178     },
53179
53180     onNodeOver : function(n, dd, e, data){
53181         var result = false;
53182         if(data.header != n){
53183             result = this.positionIndicator(data.header, n, e);
53184         }
53185         if(!result){
53186             this.proxyTop.hide();
53187             this.proxyBottom.hide();
53188         }
53189         return result ? this.dropAllowed : this.dropNotAllowed;
53190     },
53191
53192     onNodeOut : function(n, dd, e, data){
53193         this.proxyTop.hide();
53194         this.proxyBottom.hide();
53195     },
53196
53197     onNodeDrop : function(n, dd, e, data){
53198         var h = data.header;
53199         if(h != n){
53200             var cm = this.grid.colModel;
53201             var x = Roo.lib.Event.getPageX(e);
53202             var r = Roo.lib.Dom.getRegion(n.firstChild);
53203             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53204             var oldIndex = this.view.getCellIndex(h);
53205             var newIndex = this.view.getCellIndex(n);
53206             var locked = cm.isLocked(newIndex);
53207             if(pt == "after"){
53208                 newIndex++;
53209             }
53210             if(oldIndex < newIndex){
53211                 newIndex--;
53212             }
53213             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53214                 return false;
53215             }
53216             cm.setLocked(oldIndex, locked, true);
53217             cm.moveColumn(oldIndex, newIndex);
53218             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53219             return true;
53220         }
53221         return false;
53222     }
53223 });
53224 /*
53225  * Based on:
53226  * Ext JS Library 1.1.1
53227  * Copyright(c) 2006-2007, Ext JS, LLC.
53228  *
53229  * Originally Released Under LGPL - original licence link has changed is not relivant.
53230  *
53231  * Fork - LGPL
53232  * <script type="text/javascript">
53233  */
53234   
53235 /**
53236  * @class Roo.grid.GridView
53237  * @extends Roo.util.Observable
53238  *
53239  * @constructor
53240  * @param {Object} config
53241  */
53242 Roo.grid.GridView = function(config){
53243     Roo.grid.GridView.superclass.constructor.call(this);
53244     this.el = null;
53245
53246     Roo.apply(this, config);
53247 };
53248
53249 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53250
53251     unselectable :  'unselectable="on"',
53252     unselectableCls :  'x-unselectable',
53253     
53254     
53255     rowClass : "x-grid-row",
53256
53257     cellClass : "x-grid-col",
53258
53259     tdClass : "x-grid-td",
53260
53261     hdClass : "x-grid-hd",
53262
53263     splitClass : "x-grid-split",
53264
53265     sortClasses : ["sort-asc", "sort-desc"],
53266
53267     enableMoveAnim : false,
53268
53269     hlColor: "C3DAF9",
53270
53271     dh : Roo.DomHelper,
53272
53273     fly : Roo.Element.fly,
53274
53275     css : Roo.util.CSS,
53276
53277     borderWidth: 1,
53278
53279     splitOffset: 3,
53280
53281     scrollIncrement : 22,
53282
53283     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53284
53285     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53286
53287     bind : function(ds, cm){
53288         if(this.ds){
53289             this.ds.un("load", this.onLoad, this);
53290             this.ds.un("datachanged", this.onDataChange, this);
53291             this.ds.un("add", this.onAdd, this);
53292             this.ds.un("remove", this.onRemove, this);
53293             this.ds.un("update", this.onUpdate, this);
53294             this.ds.un("clear", this.onClear, this);
53295         }
53296         if(ds){
53297             ds.on("load", this.onLoad, this);
53298             ds.on("datachanged", this.onDataChange, this);
53299             ds.on("add", this.onAdd, this);
53300             ds.on("remove", this.onRemove, this);
53301             ds.on("update", this.onUpdate, this);
53302             ds.on("clear", this.onClear, this);
53303         }
53304         this.ds = ds;
53305
53306         if(this.cm){
53307             this.cm.un("widthchange", this.onColWidthChange, this);
53308             this.cm.un("headerchange", this.onHeaderChange, this);
53309             this.cm.un("hiddenchange", this.onHiddenChange, this);
53310             this.cm.un("columnmoved", this.onColumnMove, this);
53311             this.cm.un("columnlockchange", this.onColumnLock, this);
53312         }
53313         if(cm){
53314             this.generateRules(cm);
53315             cm.on("widthchange", this.onColWidthChange, this);
53316             cm.on("headerchange", this.onHeaderChange, this);
53317             cm.on("hiddenchange", this.onHiddenChange, this);
53318             cm.on("columnmoved", this.onColumnMove, this);
53319             cm.on("columnlockchange", this.onColumnLock, this);
53320         }
53321         this.cm = cm;
53322     },
53323
53324     init: function(grid){
53325         Roo.grid.GridView.superclass.init.call(this, grid);
53326
53327         this.bind(grid.dataSource, grid.colModel);
53328
53329         grid.on("headerclick", this.handleHeaderClick, this);
53330
53331         if(grid.trackMouseOver){
53332             grid.on("mouseover", this.onRowOver, this);
53333             grid.on("mouseout", this.onRowOut, this);
53334         }
53335         grid.cancelTextSelection = function(){};
53336         this.gridId = grid.id;
53337
53338         var tpls = this.templates || {};
53339
53340         if(!tpls.master){
53341             tpls.master = new Roo.Template(
53342                '<div class="x-grid" hidefocus="true">',
53343                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53344                   '<div class="x-grid-topbar"></div>',
53345                   '<div class="x-grid-scroller"><div></div></div>',
53346                   '<div class="x-grid-locked">',
53347                       '<div class="x-grid-header">{lockedHeader}</div>',
53348                       '<div class="x-grid-body">{lockedBody}</div>',
53349                   "</div>",
53350                   '<div class="x-grid-viewport">',
53351                       '<div class="x-grid-header">{header}</div>',
53352                       '<div class="x-grid-body">{body}</div>',
53353                   "</div>",
53354                   '<div class="x-grid-bottombar"></div>',
53355                  
53356                   '<div class="x-grid-resize-proxy">&#160;</div>',
53357                "</div>"
53358             );
53359             tpls.master.disableformats = true;
53360         }
53361
53362         if(!tpls.header){
53363             tpls.header = new Roo.Template(
53364                '<table border="0" cellspacing="0" cellpadding="0">',
53365                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53366                "</table>{splits}"
53367             );
53368             tpls.header.disableformats = true;
53369         }
53370         tpls.header.compile();
53371
53372         if(!tpls.hcell){
53373             tpls.hcell = new Roo.Template(
53374                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53375                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53376                 "</div></td>"
53377              );
53378              tpls.hcell.disableFormats = true;
53379         }
53380         tpls.hcell.compile();
53381
53382         if(!tpls.hsplit){
53383             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53384                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53385             tpls.hsplit.disableFormats = true;
53386         }
53387         tpls.hsplit.compile();
53388
53389         if(!tpls.body){
53390             tpls.body = new Roo.Template(
53391                '<table border="0" cellspacing="0" cellpadding="0">',
53392                "<tbody>{rows}</tbody>",
53393                "</table>"
53394             );
53395             tpls.body.disableFormats = true;
53396         }
53397         tpls.body.compile();
53398
53399         if(!tpls.row){
53400             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53401             tpls.row.disableFormats = true;
53402         }
53403         tpls.row.compile();
53404
53405         if(!tpls.cell){
53406             tpls.cell = new Roo.Template(
53407                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53408                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53409                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53410                 "</td>"
53411             );
53412             tpls.cell.disableFormats = true;
53413         }
53414         tpls.cell.compile();
53415
53416         this.templates = tpls;
53417     },
53418
53419     // remap these for backwards compat
53420     onColWidthChange : function(){
53421         this.updateColumns.apply(this, arguments);
53422     },
53423     onHeaderChange : function(){
53424         this.updateHeaders.apply(this, arguments);
53425     }, 
53426     onHiddenChange : function(){
53427         this.handleHiddenChange.apply(this, arguments);
53428     },
53429     onColumnMove : function(){
53430         this.handleColumnMove.apply(this, arguments);
53431     },
53432     onColumnLock : function(){
53433         this.handleLockChange.apply(this, arguments);
53434     },
53435
53436     onDataChange : function(){
53437         this.refresh();
53438         this.updateHeaderSortState();
53439     },
53440
53441     onClear : function(){
53442         this.refresh();
53443     },
53444
53445     onUpdate : function(ds, record){
53446         this.refreshRow(record);
53447     },
53448
53449     refreshRow : function(record){
53450         var ds = this.ds, index;
53451         if(typeof record == 'number'){
53452             index = record;
53453             record = ds.getAt(index);
53454         }else{
53455             index = ds.indexOf(record);
53456         }
53457         this.insertRows(ds, index, index, true);
53458         this.onRemove(ds, record, index+1, true);
53459         this.syncRowHeights(index, index);
53460         this.layout();
53461         this.fireEvent("rowupdated", this, index, record);
53462     },
53463
53464     onAdd : function(ds, records, index){
53465         this.insertRows(ds, index, index + (records.length-1));
53466     },
53467
53468     onRemove : function(ds, record, index, isUpdate){
53469         if(isUpdate !== true){
53470             this.fireEvent("beforerowremoved", this, index, record);
53471         }
53472         var bt = this.getBodyTable(), lt = this.getLockedTable();
53473         if(bt.rows[index]){
53474             bt.firstChild.removeChild(bt.rows[index]);
53475         }
53476         if(lt.rows[index]){
53477             lt.firstChild.removeChild(lt.rows[index]);
53478         }
53479         if(isUpdate !== true){
53480             this.stripeRows(index);
53481             this.syncRowHeights(index, index);
53482             this.layout();
53483             this.fireEvent("rowremoved", this, index, record);
53484         }
53485     },
53486
53487     onLoad : function(){
53488         this.scrollToTop();
53489     },
53490
53491     /**
53492      * Scrolls the grid to the top
53493      */
53494     scrollToTop : function(){
53495         if(this.scroller){
53496             this.scroller.dom.scrollTop = 0;
53497             this.syncScroll();
53498         }
53499     },
53500
53501     /**
53502      * Gets a panel in the header of the grid that can be used for toolbars etc.
53503      * After modifying the contents of this panel a call to grid.autoSize() may be
53504      * required to register any changes in size.
53505      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53506      * @return Roo.Element
53507      */
53508     getHeaderPanel : function(doShow){
53509         if(doShow){
53510             this.headerPanel.show();
53511         }
53512         return this.headerPanel;
53513     },
53514
53515     /**
53516      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53517      * After modifying the contents of this panel a call to grid.autoSize() may be
53518      * required to register any changes in size.
53519      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53520      * @return Roo.Element
53521      */
53522     getFooterPanel : function(doShow){
53523         if(doShow){
53524             this.footerPanel.show();
53525         }
53526         return this.footerPanel;
53527     },
53528
53529     initElements : function(){
53530         var E = Roo.Element;
53531         var el = this.grid.getGridEl().dom.firstChild;
53532         var cs = el.childNodes;
53533
53534         this.el = new E(el);
53535         
53536          this.focusEl = new E(el.firstChild);
53537         this.focusEl.swallowEvent("click", true);
53538         
53539         this.headerPanel = new E(cs[1]);
53540         this.headerPanel.enableDisplayMode("block");
53541
53542         this.scroller = new E(cs[2]);
53543         this.scrollSizer = new E(this.scroller.dom.firstChild);
53544
53545         this.lockedWrap = new E(cs[3]);
53546         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53547         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53548
53549         this.mainWrap = new E(cs[4]);
53550         this.mainHd = new E(this.mainWrap.dom.firstChild);
53551         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53552
53553         this.footerPanel = new E(cs[5]);
53554         this.footerPanel.enableDisplayMode("block");
53555
53556         this.resizeProxy = new E(cs[6]);
53557
53558         this.headerSelector = String.format(
53559            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53560            this.lockedHd.id, this.mainHd.id
53561         );
53562
53563         this.splitterSelector = String.format(
53564            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53565            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53566         );
53567     },
53568     idToCssName : function(s)
53569     {
53570         return s.replace(/[^a-z0-9]+/ig, '-');
53571     },
53572
53573     getHeaderCell : function(index){
53574         return Roo.DomQuery.select(this.headerSelector)[index];
53575     },
53576
53577     getHeaderCellMeasure : function(index){
53578         return this.getHeaderCell(index).firstChild;
53579     },
53580
53581     getHeaderCellText : function(index){
53582         return this.getHeaderCell(index).firstChild.firstChild;
53583     },
53584
53585     getLockedTable : function(){
53586         return this.lockedBody.dom.firstChild;
53587     },
53588
53589     getBodyTable : function(){
53590         return this.mainBody.dom.firstChild;
53591     },
53592
53593     getLockedRow : function(index){
53594         return this.getLockedTable().rows[index];
53595     },
53596
53597     getRow : function(index){
53598         return this.getBodyTable().rows[index];
53599     },
53600
53601     getRowComposite : function(index){
53602         if(!this.rowEl){
53603             this.rowEl = new Roo.CompositeElementLite();
53604         }
53605         var els = [], lrow, mrow;
53606         if(lrow = this.getLockedRow(index)){
53607             els.push(lrow);
53608         }
53609         if(mrow = this.getRow(index)){
53610             els.push(mrow);
53611         }
53612         this.rowEl.elements = els;
53613         return this.rowEl;
53614     },
53615     /**
53616      * Gets the 'td' of the cell
53617      * 
53618      * @param {Integer} rowIndex row to select
53619      * @param {Integer} colIndex column to select
53620      * 
53621      * @return {Object} 
53622      */
53623     getCell : function(rowIndex, colIndex){
53624         var locked = this.cm.getLockedCount();
53625         var source;
53626         if(colIndex < locked){
53627             source = this.lockedBody.dom.firstChild;
53628         }else{
53629             source = this.mainBody.dom.firstChild;
53630             colIndex -= locked;
53631         }
53632         return source.rows[rowIndex].childNodes[colIndex];
53633     },
53634
53635     getCellText : function(rowIndex, colIndex){
53636         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53637     },
53638
53639     getCellBox : function(cell){
53640         var b = this.fly(cell).getBox();
53641         if(Roo.isOpera){ // opera fails to report the Y
53642             b.y = cell.offsetTop + this.mainBody.getY();
53643         }
53644         return b;
53645     },
53646
53647     getCellIndex : function(cell){
53648         var id = String(cell.className).match(this.cellRE);
53649         if(id){
53650             return parseInt(id[1], 10);
53651         }
53652         return 0;
53653     },
53654
53655     findHeaderIndex : function(n){
53656         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53657         return r ? this.getCellIndex(r) : false;
53658     },
53659
53660     findHeaderCell : function(n){
53661         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53662         return r ? r : false;
53663     },
53664
53665     findRowIndex : function(n){
53666         if(!n){
53667             return false;
53668         }
53669         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53670         return r ? r.rowIndex : false;
53671     },
53672
53673     findCellIndex : function(node){
53674         var stop = this.el.dom;
53675         while(node && node != stop){
53676             if(this.findRE.test(node.className)){
53677                 return this.getCellIndex(node);
53678             }
53679             node = node.parentNode;
53680         }
53681         return false;
53682     },
53683
53684     getColumnId : function(index){
53685         return this.cm.getColumnId(index);
53686     },
53687
53688     getSplitters : function()
53689     {
53690         if(this.splitterSelector){
53691            return Roo.DomQuery.select(this.splitterSelector);
53692         }else{
53693             return null;
53694       }
53695     },
53696
53697     getSplitter : function(index){
53698         return this.getSplitters()[index];
53699     },
53700
53701     onRowOver : function(e, t){
53702         var row;
53703         if((row = this.findRowIndex(t)) !== false){
53704             this.getRowComposite(row).addClass("x-grid-row-over");
53705         }
53706     },
53707
53708     onRowOut : function(e, t){
53709         var row;
53710         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53711             this.getRowComposite(row).removeClass("x-grid-row-over");
53712         }
53713     },
53714
53715     renderHeaders : function(){
53716         var cm = this.cm;
53717         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53718         var cb = [], lb = [], sb = [], lsb = [], p = {};
53719         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53720             p.cellId = "x-grid-hd-0-" + i;
53721             p.splitId = "x-grid-csplit-0-" + i;
53722             p.id = cm.getColumnId(i);
53723             p.title = cm.getColumnTooltip(i) || "";
53724             p.value = cm.getColumnHeader(i) || "";
53725             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53726             if(!cm.isLocked(i)){
53727                 cb[cb.length] = ct.apply(p);
53728                 sb[sb.length] = st.apply(p);
53729             }else{
53730                 lb[lb.length] = ct.apply(p);
53731                 lsb[lsb.length] = st.apply(p);
53732             }
53733         }
53734         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53735                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53736     },
53737
53738     updateHeaders : function(){
53739         var html = this.renderHeaders();
53740         this.lockedHd.update(html[0]);
53741         this.mainHd.update(html[1]);
53742     },
53743
53744     /**
53745      * Focuses the specified row.
53746      * @param {Number} row The row index
53747      */
53748     focusRow : function(row)
53749     {
53750         //Roo.log('GridView.focusRow');
53751         var x = this.scroller.dom.scrollLeft;
53752         this.focusCell(row, 0, false);
53753         this.scroller.dom.scrollLeft = x;
53754     },
53755
53756     /**
53757      * Focuses the specified cell.
53758      * @param {Number} row The row index
53759      * @param {Number} col The column index
53760      * @param {Boolean} hscroll false to disable horizontal scrolling
53761      */
53762     focusCell : function(row, col, hscroll)
53763     {
53764         //Roo.log('GridView.focusCell');
53765         var el = this.ensureVisible(row, col, hscroll);
53766         this.focusEl.alignTo(el, "tl-tl");
53767         if(Roo.isGecko){
53768             this.focusEl.focus();
53769         }else{
53770             this.focusEl.focus.defer(1, this.focusEl);
53771         }
53772     },
53773
53774     /**
53775      * Scrolls the specified cell into view
53776      * @param {Number} row The row index
53777      * @param {Number} col The column index
53778      * @param {Boolean} hscroll false to disable horizontal scrolling
53779      */
53780     ensureVisible : function(row, col, hscroll)
53781     {
53782         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53783         //return null; //disable for testing.
53784         if(typeof row != "number"){
53785             row = row.rowIndex;
53786         }
53787         if(row < 0 && row >= this.ds.getCount()){
53788             return  null;
53789         }
53790         col = (col !== undefined ? col : 0);
53791         var cm = this.grid.colModel;
53792         while(cm.isHidden(col)){
53793             col++;
53794         }
53795
53796         var el = this.getCell(row, col);
53797         if(!el){
53798             return null;
53799         }
53800         var c = this.scroller.dom;
53801
53802         var ctop = parseInt(el.offsetTop, 10);
53803         var cleft = parseInt(el.offsetLeft, 10);
53804         var cbot = ctop + el.offsetHeight;
53805         var cright = cleft + el.offsetWidth;
53806         
53807         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53808         var stop = parseInt(c.scrollTop, 10);
53809         var sleft = parseInt(c.scrollLeft, 10);
53810         var sbot = stop + ch;
53811         var sright = sleft + c.clientWidth;
53812         /*
53813         Roo.log('GridView.ensureVisible:' +
53814                 ' ctop:' + ctop +
53815                 ' c.clientHeight:' + c.clientHeight +
53816                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53817                 ' stop:' + stop +
53818                 ' cbot:' + cbot +
53819                 ' sbot:' + sbot +
53820                 ' ch:' + ch  
53821                 );
53822         */
53823         if(ctop < stop){
53824              c.scrollTop = ctop;
53825             //Roo.log("set scrolltop to ctop DISABLE?");
53826         }else if(cbot > sbot){
53827             //Roo.log("set scrolltop to cbot-ch");
53828             c.scrollTop = cbot-ch;
53829         }
53830         
53831         if(hscroll !== false){
53832             if(cleft < sleft){
53833                 c.scrollLeft = cleft;
53834             }else if(cright > sright){
53835                 c.scrollLeft = cright-c.clientWidth;
53836             }
53837         }
53838          
53839         return el;
53840     },
53841
53842     updateColumns : function(){
53843         this.grid.stopEditing();
53844         var cm = this.grid.colModel, colIds = this.getColumnIds();
53845         //var totalWidth = cm.getTotalWidth();
53846         var pos = 0;
53847         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53848             //if(cm.isHidden(i)) continue;
53849             var w = cm.getColumnWidth(i);
53850             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53851             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53852         }
53853         this.updateSplitters();
53854     },
53855
53856     generateRules : function(cm){
53857         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53858         Roo.util.CSS.removeStyleSheet(rulesId);
53859         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53860             var cid = cm.getColumnId(i);
53861             var align = '';
53862             if(cm.config[i].align){
53863                 align = 'text-align:'+cm.config[i].align+';';
53864             }
53865             var hidden = '';
53866             if(cm.isHidden(i)){
53867                 hidden = 'display:none;';
53868             }
53869             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53870             ruleBuf.push(
53871                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53872                     this.hdSelector, cid, " {\n", align, width, "}\n",
53873                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53874                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53875         }
53876         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53877     },
53878
53879     updateSplitters : function(){
53880         var cm = this.cm, s = this.getSplitters();
53881         if(s){ // splitters not created yet
53882             var pos = 0, locked = true;
53883             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53884                 if(cm.isHidden(i)) continue;
53885                 var w = cm.getColumnWidth(i); // make sure it's a number
53886                 if(!cm.isLocked(i) && locked){
53887                     pos = 0;
53888                     locked = false;
53889                 }
53890                 pos += w;
53891                 s[i].style.left = (pos-this.splitOffset) + "px";
53892             }
53893         }
53894     },
53895
53896     handleHiddenChange : function(colModel, colIndex, hidden){
53897         if(hidden){
53898             this.hideColumn(colIndex);
53899         }else{
53900             this.unhideColumn(colIndex);
53901         }
53902     },
53903
53904     hideColumn : function(colIndex){
53905         var cid = this.getColumnId(colIndex);
53906         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53907         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53908         if(Roo.isSafari){
53909             this.updateHeaders();
53910         }
53911         this.updateSplitters();
53912         this.layout();
53913     },
53914
53915     unhideColumn : function(colIndex){
53916         var cid = this.getColumnId(colIndex);
53917         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53918         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53919
53920         if(Roo.isSafari){
53921             this.updateHeaders();
53922         }
53923         this.updateSplitters();
53924         this.layout();
53925     },
53926
53927     insertRows : function(dm, firstRow, lastRow, isUpdate){
53928         if(firstRow == 0 && lastRow == dm.getCount()-1){
53929             this.refresh();
53930         }else{
53931             if(!isUpdate){
53932                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53933             }
53934             var s = this.getScrollState();
53935             var markup = this.renderRows(firstRow, lastRow);
53936             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53937             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53938             this.restoreScroll(s);
53939             if(!isUpdate){
53940                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53941                 this.syncRowHeights(firstRow, lastRow);
53942                 this.stripeRows(firstRow);
53943                 this.layout();
53944             }
53945         }
53946     },
53947
53948     bufferRows : function(markup, target, index){
53949         var before = null, trows = target.rows, tbody = target.tBodies[0];
53950         if(index < trows.length){
53951             before = trows[index];
53952         }
53953         var b = document.createElement("div");
53954         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53955         var rows = b.firstChild.rows;
53956         for(var i = 0, len = rows.length; i < len; i++){
53957             if(before){
53958                 tbody.insertBefore(rows[0], before);
53959             }else{
53960                 tbody.appendChild(rows[0]);
53961             }
53962         }
53963         b.innerHTML = "";
53964         b = null;
53965     },
53966
53967     deleteRows : function(dm, firstRow, lastRow){
53968         if(dm.getRowCount()<1){
53969             this.fireEvent("beforerefresh", this);
53970             this.mainBody.update("");
53971             this.lockedBody.update("");
53972             this.fireEvent("refresh", this);
53973         }else{
53974             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53975             var bt = this.getBodyTable();
53976             var tbody = bt.firstChild;
53977             var rows = bt.rows;
53978             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53979                 tbody.removeChild(rows[firstRow]);
53980             }
53981             this.stripeRows(firstRow);
53982             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53983         }
53984     },
53985
53986     updateRows : function(dataSource, firstRow, lastRow){
53987         var s = this.getScrollState();
53988         this.refresh();
53989         this.restoreScroll(s);
53990     },
53991
53992     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
53993         if(!noRefresh){
53994            this.refresh();
53995         }
53996         this.updateHeaderSortState();
53997     },
53998
53999     getScrollState : function(){
54000         
54001         var sb = this.scroller.dom;
54002         return {left: sb.scrollLeft, top: sb.scrollTop};
54003     },
54004
54005     stripeRows : function(startRow){
54006         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54007             return;
54008         }
54009         startRow = startRow || 0;
54010         var rows = this.getBodyTable().rows;
54011         var lrows = this.getLockedTable().rows;
54012         var cls = ' x-grid-row-alt ';
54013         for(var i = startRow, len = rows.length; i < len; i++){
54014             var row = rows[i], lrow = lrows[i];
54015             var isAlt = ((i+1) % 2 == 0);
54016             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54017             if(isAlt == hasAlt){
54018                 continue;
54019             }
54020             if(isAlt){
54021                 row.className += " x-grid-row-alt";
54022             }else{
54023                 row.className = row.className.replace("x-grid-row-alt", "");
54024             }
54025             if(lrow){
54026                 lrow.className = row.className;
54027             }
54028         }
54029     },
54030
54031     restoreScroll : function(state){
54032         //Roo.log('GridView.restoreScroll');
54033         var sb = this.scroller.dom;
54034         sb.scrollLeft = state.left;
54035         sb.scrollTop = state.top;
54036         this.syncScroll();
54037     },
54038
54039     syncScroll : function(){
54040         //Roo.log('GridView.syncScroll');
54041         var sb = this.scroller.dom;
54042         var sh = this.mainHd.dom;
54043         var bs = this.mainBody.dom;
54044         var lv = this.lockedBody.dom;
54045         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54046         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54047     },
54048
54049     handleScroll : function(e){
54050         this.syncScroll();
54051         var sb = this.scroller.dom;
54052         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54053         e.stopEvent();
54054     },
54055
54056     handleWheel : function(e){
54057         var d = e.getWheelDelta();
54058         this.scroller.dom.scrollTop -= d*22;
54059         // set this here to prevent jumpy scrolling on large tables
54060         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54061         e.stopEvent();
54062     },
54063
54064     renderRows : function(startRow, endRow){
54065         // pull in all the crap needed to render rows
54066         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54067         var colCount = cm.getColumnCount();
54068
54069         if(ds.getCount() < 1){
54070             return ["", ""];
54071         }
54072
54073         // build a map for all the columns
54074         var cs = [];
54075         for(var i = 0; i < colCount; i++){
54076             var name = cm.getDataIndex(i);
54077             cs[i] = {
54078                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54079                 renderer : cm.getRenderer(i),
54080                 id : cm.getColumnId(i),
54081                 locked : cm.isLocked(i)
54082             };
54083         }
54084
54085         startRow = startRow || 0;
54086         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54087
54088         // records to render
54089         var rs = ds.getRange(startRow, endRow);
54090
54091         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54092     },
54093
54094     // As much as I hate to duplicate code, this was branched because FireFox really hates
54095     // [].join("") on strings. The performance difference was substantial enough to
54096     // branch this function
54097     doRender : Roo.isGecko ?
54098             function(cs, rs, ds, startRow, colCount, stripe){
54099                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54100                 // buffers
54101                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54102                 
54103                 var hasListener = this.grid.hasListener('rowclass');
54104                 var rowcfg = {};
54105                 for(var j = 0, len = rs.length; j < len; j++){
54106                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54107                     for(var i = 0; i < colCount; i++){
54108                         c = cs[i];
54109                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54110                         p.id = c.id;
54111                         p.css = p.attr = "";
54112                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54113                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54114                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54115                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54116                         }
54117                         var markup = ct.apply(p);
54118                         if(!c.locked){
54119                             cb+= markup;
54120                         }else{
54121                             lcb+= markup;
54122                         }
54123                     }
54124                     var alt = [];
54125                     if(stripe && ((rowIndex+1) % 2 == 0)){
54126                         alt.push("x-grid-row-alt")
54127                     }
54128                     if(r.dirty){
54129                         alt.push(  " x-grid-dirty-row");
54130                     }
54131                     rp.cells = lcb;
54132                     if(this.getRowClass){
54133                         alt.push(this.getRowClass(r, rowIndex));
54134                     }
54135                     if (hasListener) {
54136                         rowcfg = {
54137                              
54138                             record: r,
54139                             rowIndex : rowIndex,
54140                             rowClass : ''
54141                         }
54142                         this.grid.fireEvent('rowclass', this, rowcfg);
54143                         alt.push(rowcfg.rowClass);
54144                     }
54145                     rp.alt = alt.join(" ");
54146                     lbuf+= rt.apply(rp);
54147                     rp.cells = cb;
54148                     buf+=  rt.apply(rp);
54149                 }
54150                 return [lbuf, buf];
54151             } :
54152             function(cs, rs, ds, startRow, colCount, stripe){
54153                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54154                 // buffers
54155                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54156                 var hasListener = this.grid.hasListener('rowclass');
54157  
54158                 var rowcfg = {};
54159                 for(var j = 0, len = rs.length; j < len; j++){
54160                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54161                     for(var i = 0; i < colCount; i++){
54162                         c = cs[i];
54163                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54164                         p.id = c.id;
54165                         p.css = p.attr = "";
54166                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54167                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54168                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54169                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54170                         }
54171                         
54172                         var markup = ct.apply(p);
54173                         if(!c.locked){
54174                             cb[cb.length] = markup;
54175                         }else{
54176                             lcb[lcb.length] = markup;
54177                         }
54178                     }
54179                     var alt = [];
54180                     if(stripe && ((rowIndex+1) % 2 == 0)){
54181                         alt.push( "x-grid-row-alt");
54182                     }
54183                     if(r.dirty){
54184                         alt.push(" x-grid-dirty-row");
54185                     }
54186                     rp.cells = lcb;
54187                     if(this.getRowClass){
54188                         alt.push( this.getRowClass(r, rowIndex));
54189                     }
54190                     if (hasListener) {
54191                         rowcfg = {
54192                              
54193                             record: r,
54194                             rowIndex : rowIndex,
54195                             rowClass : ''
54196                         }
54197                         this.grid.fireEvent('rowclass', this, rowcfg);
54198                         alt.push(rowcfg.rowClass);
54199                     }
54200                     rp.alt = alt.join(" ");
54201                     rp.cells = lcb.join("");
54202                     lbuf[lbuf.length] = rt.apply(rp);
54203                     rp.cells = cb.join("");
54204                     buf[buf.length] =  rt.apply(rp);
54205                 }
54206                 return [lbuf.join(""), buf.join("")];
54207             },
54208
54209     renderBody : function(){
54210         var markup = this.renderRows();
54211         var bt = this.templates.body;
54212         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54213     },
54214
54215     /**
54216      * Refreshes the grid
54217      * @param {Boolean} headersToo
54218      */
54219     refresh : function(headersToo){
54220         this.fireEvent("beforerefresh", this);
54221         this.grid.stopEditing();
54222         var result = this.renderBody();
54223         this.lockedBody.update(result[0]);
54224         this.mainBody.update(result[1]);
54225         if(headersToo === true){
54226             this.updateHeaders();
54227             this.updateColumns();
54228             this.updateSplitters();
54229             this.updateHeaderSortState();
54230         }
54231         this.syncRowHeights();
54232         this.layout();
54233         this.fireEvent("refresh", this);
54234     },
54235
54236     handleColumnMove : function(cm, oldIndex, newIndex){
54237         this.indexMap = null;
54238         var s = this.getScrollState();
54239         this.refresh(true);
54240         this.restoreScroll(s);
54241         this.afterMove(newIndex);
54242     },
54243
54244     afterMove : function(colIndex){
54245         if(this.enableMoveAnim && Roo.enableFx){
54246             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54247         }
54248         // if multisort - fix sortOrder, and reload..
54249         if (this.grid.dataSource.multiSort) {
54250             // the we can call sort again..
54251             var dm = this.grid.dataSource;
54252             var cm = this.grid.colModel;
54253             var so = [];
54254             for(var i = 0; i < cm.config.length; i++ ) {
54255                 
54256                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54257                     continue; // dont' bother, it's not in sort list or being set.
54258                 }
54259                 
54260                 so.push(cm.config[i].dataIndex);
54261             };
54262             dm.sortOrder = so;
54263             dm.load(dm.lastOptions);
54264             
54265             
54266         }
54267         
54268     },
54269
54270     updateCell : function(dm, rowIndex, dataIndex){
54271         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54272         if(typeof colIndex == "undefined"){ // not present in grid
54273             return;
54274         }
54275         var cm = this.grid.colModel;
54276         var cell = this.getCell(rowIndex, colIndex);
54277         var cellText = this.getCellText(rowIndex, colIndex);
54278
54279         var p = {
54280             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54281             id : cm.getColumnId(colIndex),
54282             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54283         };
54284         var renderer = cm.getRenderer(colIndex);
54285         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54286         if(typeof val == "undefined" || val === "") val = "&#160;";
54287         cellText.innerHTML = val;
54288         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54289         this.syncRowHeights(rowIndex, rowIndex);
54290     },
54291
54292     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54293         var maxWidth = 0;
54294         if(this.grid.autoSizeHeaders){
54295             var h = this.getHeaderCellMeasure(colIndex);
54296             maxWidth = Math.max(maxWidth, h.scrollWidth);
54297         }
54298         var tb, index;
54299         if(this.cm.isLocked(colIndex)){
54300             tb = this.getLockedTable();
54301             index = colIndex;
54302         }else{
54303             tb = this.getBodyTable();
54304             index = colIndex - this.cm.getLockedCount();
54305         }
54306         if(tb && tb.rows){
54307             var rows = tb.rows;
54308             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54309             for(var i = 0; i < stopIndex; i++){
54310                 var cell = rows[i].childNodes[index].firstChild;
54311                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54312             }
54313         }
54314         return maxWidth + /*margin for error in IE*/ 5;
54315     },
54316     /**
54317      * Autofit a column to its content.
54318      * @param {Number} colIndex
54319      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54320      */
54321      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54322          if(this.cm.isHidden(colIndex)){
54323              return; // can't calc a hidden column
54324          }
54325         if(forceMinSize){
54326             var cid = this.cm.getColumnId(colIndex);
54327             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54328            if(this.grid.autoSizeHeaders){
54329                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54330            }
54331         }
54332         var newWidth = this.calcColumnWidth(colIndex);
54333         this.cm.setColumnWidth(colIndex,
54334             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54335         if(!suppressEvent){
54336             this.grid.fireEvent("columnresize", colIndex, newWidth);
54337         }
54338     },
54339
54340     /**
54341      * Autofits all columns to their content and then expands to fit any extra space in the grid
54342      */
54343      autoSizeColumns : function(){
54344         var cm = this.grid.colModel;
54345         var colCount = cm.getColumnCount();
54346         for(var i = 0; i < colCount; i++){
54347             this.autoSizeColumn(i, true, true);
54348         }
54349         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54350             this.fitColumns();
54351         }else{
54352             this.updateColumns();
54353             this.layout();
54354         }
54355     },
54356
54357     /**
54358      * Autofits all columns to the grid's width proportionate with their current size
54359      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54360      */
54361     fitColumns : function(reserveScrollSpace){
54362         var cm = this.grid.colModel;
54363         var colCount = cm.getColumnCount();
54364         var cols = [];
54365         var width = 0;
54366         var i, w;
54367         for (i = 0; i < colCount; i++){
54368             if(!cm.isHidden(i) && !cm.isFixed(i)){
54369                 w = cm.getColumnWidth(i);
54370                 cols.push(i);
54371                 cols.push(w);
54372                 width += w;
54373             }
54374         }
54375         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54376         if(reserveScrollSpace){
54377             avail -= 17;
54378         }
54379         var frac = (avail - cm.getTotalWidth())/width;
54380         while (cols.length){
54381             w = cols.pop();
54382             i = cols.pop();
54383             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54384         }
54385         this.updateColumns();
54386         this.layout();
54387     },
54388
54389     onRowSelect : function(rowIndex){
54390         var row = this.getRowComposite(rowIndex);
54391         row.addClass("x-grid-row-selected");
54392     },
54393
54394     onRowDeselect : function(rowIndex){
54395         var row = this.getRowComposite(rowIndex);
54396         row.removeClass("x-grid-row-selected");
54397     },
54398
54399     onCellSelect : function(row, col){
54400         var cell = this.getCell(row, col);
54401         if(cell){
54402             Roo.fly(cell).addClass("x-grid-cell-selected");
54403         }
54404     },
54405
54406     onCellDeselect : function(row, col){
54407         var cell = this.getCell(row, col);
54408         if(cell){
54409             Roo.fly(cell).removeClass("x-grid-cell-selected");
54410         }
54411     },
54412
54413     updateHeaderSortState : function(){
54414         
54415         // sort state can be single { field: xxx, direction : yyy}
54416         // or   { xxx=>ASC , yyy : DESC ..... }
54417         
54418         var mstate = {};
54419         if (!this.ds.multiSort) { 
54420             var state = this.ds.getSortState();
54421             if(!state){
54422                 return;
54423             }
54424             mstate[state.field] = state.direction;
54425             // FIXME... - this is not used here.. but might be elsewhere..
54426             this.sortState = state;
54427             
54428         } else {
54429             mstate = this.ds.sortToggle;
54430         }
54431         //remove existing sort classes..
54432         
54433         var sc = this.sortClasses;
54434         var hds = this.el.select(this.headerSelector).removeClass(sc);
54435         
54436         for(var f in mstate) {
54437         
54438             var sortColumn = this.cm.findColumnIndex(f);
54439             
54440             if(sortColumn != -1){
54441                 var sortDir = mstate[f];        
54442                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54443             }
54444         }
54445         
54446          
54447         
54448     },
54449
54450
54451     handleHeaderClick : function(g, index,e){
54452         
54453         Roo.log("header click");
54454         
54455         if (Roo.isTouch) {
54456             // touch events on header are handled by context
54457             this.handleHdCtx(g,index,e);
54458             return;
54459         }
54460         
54461         
54462         if(this.headersDisabled){
54463             return;
54464         }
54465         var dm = g.dataSource, cm = g.colModel;
54466         if(!cm.isSortable(index)){
54467             return;
54468         }
54469         g.stopEditing();
54470         
54471         if (dm.multiSort) {
54472             // update the sortOrder
54473             var so = [];
54474             for(var i = 0; i < cm.config.length; i++ ) {
54475                 
54476                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54477                     continue; // dont' bother, it's not in sort list or being set.
54478                 }
54479                 
54480                 so.push(cm.config[i].dataIndex);
54481             };
54482             dm.sortOrder = so;
54483         }
54484         
54485         
54486         dm.sort(cm.getDataIndex(index));
54487     },
54488
54489
54490     destroy : function(){
54491         if(this.colMenu){
54492             this.colMenu.removeAll();
54493             Roo.menu.MenuMgr.unregister(this.colMenu);
54494             this.colMenu.getEl().remove();
54495             delete this.colMenu;
54496         }
54497         if(this.hmenu){
54498             this.hmenu.removeAll();
54499             Roo.menu.MenuMgr.unregister(this.hmenu);
54500             this.hmenu.getEl().remove();
54501             delete this.hmenu;
54502         }
54503         if(this.grid.enableColumnMove){
54504             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54505             if(dds){
54506                 for(var dd in dds){
54507                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54508                         var elid = dds[dd].dragElId;
54509                         dds[dd].unreg();
54510                         Roo.get(elid).remove();
54511                     } else if(dds[dd].config.isTarget){
54512                         dds[dd].proxyTop.remove();
54513                         dds[dd].proxyBottom.remove();
54514                         dds[dd].unreg();
54515                     }
54516                     if(Roo.dd.DDM.locationCache[dd]){
54517                         delete Roo.dd.DDM.locationCache[dd];
54518                     }
54519                 }
54520                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54521             }
54522         }
54523         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54524         this.bind(null, null);
54525         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54526     },
54527
54528     handleLockChange : function(){
54529         this.refresh(true);
54530     },
54531
54532     onDenyColumnLock : function(){
54533
54534     },
54535
54536     onDenyColumnHide : function(){
54537
54538     },
54539
54540     handleHdMenuClick : function(item){
54541         var index = this.hdCtxIndex;
54542         var cm = this.cm, ds = this.ds;
54543         switch(item.id){
54544             case "asc":
54545                 ds.sort(cm.getDataIndex(index), "ASC");
54546                 break;
54547             case "desc":
54548                 ds.sort(cm.getDataIndex(index), "DESC");
54549                 break;
54550             case "lock":
54551                 var lc = cm.getLockedCount();
54552                 if(cm.getColumnCount(true) <= lc+1){
54553                     this.onDenyColumnLock();
54554                     return;
54555                 }
54556                 if(lc != index){
54557                     cm.setLocked(index, true, true);
54558                     cm.moveColumn(index, lc);
54559                     this.grid.fireEvent("columnmove", index, lc);
54560                 }else{
54561                     cm.setLocked(index, true);
54562                 }
54563             break;
54564             case "unlock":
54565                 var lc = cm.getLockedCount();
54566                 if((lc-1) != index){
54567                     cm.setLocked(index, false, true);
54568                     cm.moveColumn(index, lc-1);
54569                     this.grid.fireEvent("columnmove", index, lc-1);
54570                 }else{
54571                     cm.setLocked(index, false);
54572                 }
54573             break;
54574             case 'wider': // used to expand cols on touch..
54575             case 'narrow':
54576                 var cw = cm.getColumnWidth(index);
54577                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54578                 cw = Math.max(0, cw);
54579                 cw = Math.min(cw,4000);
54580                 cm.setColumnWidth(index, cw);
54581                 break;
54582                 
54583             default:
54584                 index = cm.getIndexById(item.id.substr(4));
54585                 if(index != -1){
54586                     if(item.checked && cm.getColumnCount(true) <= 1){
54587                         this.onDenyColumnHide();
54588                         return false;
54589                     }
54590                     cm.setHidden(index, item.checked);
54591                 }
54592         }
54593         return true;
54594     },
54595
54596     beforeColMenuShow : function(){
54597         var cm = this.cm,  colCount = cm.getColumnCount();
54598         this.colMenu.removeAll();
54599         for(var i = 0; i < colCount; i++){
54600             this.colMenu.add(new Roo.menu.CheckItem({
54601                 id: "col-"+cm.getColumnId(i),
54602                 text: cm.getColumnHeader(i),
54603                 checked: !cm.isHidden(i),
54604                 hideOnClick:false
54605             }));
54606         }
54607     },
54608
54609     handleHdCtx : function(g, index, e){
54610         e.stopEvent();
54611         var hd = this.getHeaderCell(index);
54612         this.hdCtxIndex = index;
54613         var ms = this.hmenu.items, cm = this.cm;
54614         ms.get("asc").setDisabled(!cm.isSortable(index));
54615         ms.get("desc").setDisabled(!cm.isSortable(index));
54616         if(this.grid.enableColLock !== false){
54617             ms.get("lock").setDisabled(cm.isLocked(index));
54618             ms.get("unlock").setDisabled(!cm.isLocked(index));
54619         }
54620         this.hmenu.show(hd, "tl-bl");
54621     },
54622
54623     handleHdOver : function(e){
54624         var hd = this.findHeaderCell(e.getTarget());
54625         if(hd && !this.headersDisabled){
54626             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54627                this.fly(hd).addClass("x-grid-hd-over");
54628             }
54629         }
54630     },
54631
54632     handleHdOut : function(e){
54633         var hd = this.findHeaderCell(e.getTarget());
54634         if(hd){
54635             this.fly(hd).removeClass("x-grid-hd-over");
54636         }
54637     },
54638
54639     handleSplitDblClick : function(e, t){
54640         var i = this.getCellIndex(t);
54641         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54642             this.autoSizeColumn(i, true);
54643             this.layout();
54644         }
54645     },
54646
54647     render : function(){
54648
54649         var cm = this.cm;
54650         var colCount = cm.getColumnCount();
54651
54652         if(this.grid.monitorWindowResize === true){
54653             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54654         }
54655         var header = this.renderHeaders();
54656         var body = this.templates.body.apply({rows:""});
54657         var html = this.templates.master.apply({
54658             lockedBody: body,
54659             body: body,
54660             lockedHeader: header[0],
54661             header: header[1]
54662         });
54663
54664         //this.updateColumns();
54665
54666         this.grid.getGridEl().dom.innerHTML = html;
54667
54668         this.initElements();
54669         
54670         // a kludge to fix the random scolling effect in webkit
54671         this.el.on("scroll", function() {
54672             this.el.dom.scrollTop=0; // hopefully not recursive..
54673         },this);
54674
54675         this.scroller.on("scroll", this.handleScroll, this);
54676         this.lockedBody.on("mousewheel", this.handleWheel, this);
54677         this.mainBody.on("mousewheel", this.handleWheel, this);
54678
54679         this.mainHd.on("mouseover", this.handleHdOver, this);
54680         this.mainHd.on("mouseout", this.handleHdOut, this);
54681         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54682                 {delegate: "."+this.splitClass});
54683
54684         this.lockedHd.on("mouseover", this.handleHdOver, this);
54685         this.lockedHd.on("mouseout", this.handleHdOut, this);
54686         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54687                 {delegate: "."+this.splitClass});
54688
54689         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54690             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54691         }
54692
54693         this.updateSplitters();
54694
54695         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54696             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54697             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54698         }
54699
54700         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54701             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54702             this.hmenu.add(
54703                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54704                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54705             );
54706             if(this.grid.enableColLock !== false){
54707                 this.hmenu.add('-',
54708                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54709                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54710                 );
54711             }
54712             if (Roo.isTouch) {
54713                  this.hmenu.add('-',
54714                     {id:"wider", text: this.columnsWiderText},
54715                     {id:"narrow", text: this.columnsNarrowText }
54716                 );
54717                 
54718                  
54719             }
54720             
54721             if(this.grid.enableColumnHide !== false){
54722
54723                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54724                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54725                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54726
54727                 this.hmenu.add('-',
54728                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54729                 );
54730             }
54731             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54732
54733             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54734         }
54735
54736         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54737             this.dd = new Roo.grid.GridDragZone(this.grid, {
54738                 ddGroup : this.grid.ddGroup || 'GridDD'
54739             });
54740             
54741         }
54742
54743         /*
54744         for(var i = 0; i < colCount; i++){
54745             if(cm.isHidden(i)){
54746                 this.hideColumn(i);
54747             }
54748             if(cm.config[i].align){
54749                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54750                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54751             }
54752         }*/
54753         
54754         this.updateHeaderSortState();
54755
54756         this.beforeInitialResize();
54757         this.layout(true);
54758
54759         // two part rendering gives faster view to the user
54760         this.renderPhase2.defer(1, this);
54761     },
54762
54763     renderPhase2 : function(){
54764         // render the rows now
54765         this.refresh();
54766         if(this.grid.autoSizeColumns){
54767             this.autoSizeColumns();
54768         }
54769     },
54770
54771     beforeInitialResize : function(){
54772
54773     },
54774
54775     onColumnSplitterMoved : function(i, w){
54776         this.userResized = true;
54777         var cm = this.grid.colModel;
54778         cm.setColumnWidth(i, w, true);
54779         var cid = cm.getColumnId(i);
54780         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54781         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54782         this.updateSplitters();
54783         this.layout();
54784         this.grid.fireEvent("columnresize", i, w);
54785     },
54786
54787     syncRowHeights : function(startIndex, endIndex){
54788         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54789             startIndex = startIndex || 0;
54790             var mrows = this.getBodyTable().rows;
54791             var lrows = this.getLockedTable().rows;
54792             var len = mrows.length-1;
54793             endIndex = Math.min(endIndex || len, len);
54794             for(var i = startIndex; i <= endIndex; i++){
54795                 var m = mrows[i], l = lrows[i];
54796                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54797                 m.style.height = l.style.height = h + "px";
54798             }
54799         }
54800     },
54801
54802     layout : function(initialRender, is2ndPass){
54803         var g = this.grid;
54804         var auto = g.autoHeight;
54805         var scrollOffset = 16;
54806         var c = g.getGridEl(), cm = this.cm,
54807                 expandCol = g.autoExpandColumn,
54808                 gv = this;
54809         //c.beginMeasure();
54810
54811         if(!c.dom.offsetWidth){ // display:none?
54812             if(initialRender){
54813                 this.lockedWrap.show();
54814                 this.mainWrap.show();
54815             }
54816             return;
54817         }
54818
54819         var hasLock = this.cm.isLocked(0);
54820
54821         var tbh = this.headerPanel.getHeight();
54822         var bbh = this.footerPanel.getHeight();
54823
54824         if(auto){
54825             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54826             var newHeight = ch + c.getBorderWidth("tb");
54827             if(g.maxHeight){
54828                 newHeight = Math.min(g.maxHeight, newHeight);
54829             }
54830             c.setHeight(newHeight);
54831         }
54832
54833         if(g.autoWidth){
54834             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54835         }
54836
54837         var s = this.scroller;
54838
54839         var csize = c.getSize(true);
54840
54841         this.el.setSize(csize.width, csize.height);
54842
54843         this.headerPanel.setWidth(csize.width);
54844         this.footerPanel.setWidth(csize.width);
54845
54846         var hdHeight = this.mainHd.getHeight();
54847         var vw = csize.width;
54848         var vh = csize.height - (tbh + bbh);
54849
54850         s.setSize(vw, vh);
54851
54852         var bt = this.getBodyTable();
54853         var ltWidth = hasLock ?
54854                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54855
54856         var scrollHeight = bt.offsetHeight;
54857         var scrollWidth = ltWidth + bt.offsetWidth;
54858         var vscroll = false, hscroll = false;
54859
54860         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54861
54862         var lw = this.lockedWrap, mw = this.mainWrap;
54863         var lb = this.lockedBody, mb = this.mainBody;
54864
54865         setTimeout(function(){
54866             var t = s.dom.offsetTop;
54867             var w = s.dom.clientWidth,
54868                 h = s.dom.clientHeight;
54869
54870             lw.setTop(t);
54871             lw.setSize(ltWidth, h);
54872
54873             mw.setLeftTop(ltWidth, t);
54874             mw.setSize(w-ltWidth, h);
54875
54876             lb.setHeight(h-hdHeight);
54877             mb.setHeight(h-hdHeight);
54878
54879             if(is2ndPass !== true && !gv.userResized && expandCol){
54880                 // high speed resize without full column calculation
54881                 
54882                 var ci = cm.getIndexById(expandCol);
54883                 if (ci < 0) {
54884                     ci = cm.findColumnIndex(expandCol);
54885                 }
54886                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54887                 var expandId = cm.getColumnId(ci);
54888                 var  tw = cm.getTotalWidth(false);
54889                 var currentWidth = cm.getColumnWidth(ci);
54890                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54891                 if(currentWidth != cw){
54892                     cm.setColumnWidth(ci, cw, true);
54893                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54894                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54895                     gv.updateSplitters();
54896                     gv.layout(false, true);
54897                 }
54898             }
54899
54900             if(initialRender){
54901                 lw.show();
54902                 mw.show();
54903             }
54904             //c.endMeasure();
54905         }, 10);
54906     },
54907
54908     onWindowResize : function(){
54909         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54910             return;
54911         }
54912         this.layout();
54913     },
54914
54915     appendFooter : function(parentEl){
54916         return null;
54917     },
54918
54919     sortAscText : "Sort Ascending",
54920     sortDescText : "Sort Descending",
54921     lockText : "Lock Column",
54922     unlockText : "Unlock Column",
54923     columnsText : "Columns",
54924  
54925     columnsWiderText : "Wider",
54926     columnsNarrowText : "Thinner"
54927 });
54928
54929
54930 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54931     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54932     this.proxy.el.addClass('x-grid3-col-dd');
54933 };
54934
54935 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54936     handleMouseDown : function(e){
54937
54938     },
54939
54940     callHandleMouseDown : function(e){
54941         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54942     }
54943 });
54944 /*
54945  * Based on:
54946  * Ext JS Library 1.1.1
54947  * Copyright(c) 2006-2007, Ext JS, LLC.
54948  *
54949  * Originally Released Under LGPL - original licence link has changed is not relivant.
54950  *
54951  * Fork - LGPL
54952  * <script type="text/javascript">
54953  */
54954  
54955 // private
54956 // This is a support class used internally by the Grid components
54957 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54958     this.grid = grid;
54959     this.view = grid.getView();
54960     this.proxy = this.view.resizeProxy;
54961     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54962         "gridSplitters" + this.grid.getGridEl().id, {
54963         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54964     });
54965     this.setHandleElId(Roo.id(hd));
54966     this.setOuterHandleElId(Roo.id(hd2));
54967     this.scroll = false;
54968 };
54969 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54970     fly: Roo.Element.fly,
54971
54972     b4StartDrag : function(x, y){
54973         this.view.headersDisabled = true;
54974         this.proxy.setHeight(this.view.mainWrap.getHeight());
54975         var w = this.cm.getColumnWidth(this.cellIndex);
54976         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54977         this.resetConstraints();
54978         this.setXConstraint(minw, 1000);
54979         this.setYConstraint(0, 0);
54980         this.minX = x - minw;
54981         this.maxX = x + 1000;
54982         this.startPos = x;
54983         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54984     },
54985
54986
54987     handleMouseDown : function(e){
54988         ev = Roo.EventObject.setEvent(e);
54989         var t = this.fly(ev.getTarget());
54990         if(t.hasClass("x-grid-split")){
54991             this.cellIndex = this.view.getCellIndex(t.dom);
54992             this.split = t.dom;
54993             this.cm = this.grid.colModel;
54994             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
54995                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
54996             }
54997         }
54998     },
54999
55000     endDrag : function(e){
55001         this.view.headersDisabled = false;
55002         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55003         var diff = endX - this.startPos;
55004         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55005     },
55006
55007     autoOffset : function(){
55008         this.setDelta(0,0);
55009     }
55010 });/*
55011  * Based on:
55012  * Ext JS Library 1.1.1
55013  * Copyright(c) 2006-2007, Ext JS, LLC.
55014  *
55015  * Originally Released Under LGPL - original licence link has changed is not relivant.
55016  *
55017  * Fork - LGPL
55018  * <script type="text/javascript">
55019  */
55020  
55021 // private
55022 // This is a support class used internally by the Grid components
55023 Roo.grid.GridDragZone = function(grid, config){
55024     this.view = grid.getView();
55025     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55026     if(this.view.lockedBody){
55027         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55028         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55029     }
55030     this.scroll = false;
55031     this.grid = grid;
55032     this.ddel = document.createElement('div');
55033     this.ddel.className = 'x-grid-dd-wrap';
55034 };
55035
55036 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55037     ddGroup : "GridDD",
55038
55039     getDragData : function(e){
55040         var t = Roo.lib.Event.getTarget(e);
55041         var rowIndex = this.view.findRowIndex(t);
55042         var sm = this.grid.selModel;
55043             
55044         //Roo.log(rowIndex);
55045         
55046         if (sm.getSelectedCell) {
55047             // cell selection..
55048             if (!sm.getSelectedCell()) {
55049                 return false;
55050             }
55051             if (rowIndex != sm.getSelectedCell()[0]) {
55052                 return false;
55053             }
55054         
55055         }
55056         
55057         if(rowIndex !== false){
55058             
55059             // if editorgrid.. 
55060             
55061             
55062             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55063                
55064             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55065               //  
55066             //}
55067             if (e.hasModifier()){
55068                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55069             }
55070             
55071             Roo.log("getDragData");
55072             
55073             return {
55074                 grid: this.grid,
55075                 ddel: this.ddel,
55076                 rowIndex: rowIndex,
55077                 selections:sm.getSelections ? sm.getSelections() : (
55078                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55079                 )
55080             };
55081         }
55082         return false;
55083     },
55084
55085     onInitDrag : function(e){
55086         var data = this.dragData;
55087         this.ddel.innerHTML = this.grid.getDragDropText();
55088         this.proxy.update(this.ddel);
55089         // fire start drag?
55090     },
55091
55092     afterRepair : function(){
55093         this.dragging = false;
55094     },
55095
55096     getRepairXY : function(e, data){
55097         return false;
55098     },
55099
55100     onEndDrag : function(data, e){
55101         // fire end drag?
55102     },
55103
55104     onValidDrop : function(dd, e, id){
55105         // fire drag drop?
55106         this.hideProxy();
55107     },
55108
55109     beforeInvalidDrop : function(e, id){
55110
55111     }
55112 });/*
55113  * Based on:
55114  * Ext JS Library 1.1.1
55115  * Copyright(c) 2006-2007, Ext JS, LLC.
55116  *
55117  * Originally Released Under LGPL - original licence link has changed is not relivant.
55118  *
55119  * Fork - LGPL
55120  * <script type="text/javascript">
55121  */
55122  
55123
55124 /**
55125  * @class Roo.grid.ColumnModel
55126  * @extends Roo.util.Observable
55127  * This is the default implementation of a ColumnModel used by the Grid. It defines
55128  * the columns in the grid.
55129  * <br>Usage:<br>
55130  <pre><code>
55131  var colModel = new Roo.grid.ColumnModel([
55132         {header: "Ticker", width: 60, sortable: true, locked: true},
55133         {header: "Company Name", width: 150, sortable: true},
55134         {header: "Market Cap.", width: 100, sortable: true},
55135         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55136         {header: "Employees", width: 100, sortable: true, resizable: false}
55137  ]);
55138  </code></pre>
55139  * <p>
55140  
55141  * The config options listed for this class are options which may appear in each
55142  * individual column definition.
55143  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55144  * @constructor
55145  * @param {Object} config An Array of column config objects. See this class's
55146  * config objects for details.
55147 */
55148 Roo.grid.ColumnModel = function(config){
55149         /**
55150      * The config passed into the constructor
55151      */
55152     this.config = config;
55153     this.lookup = {};
55154
55155     // if no id, create one
55156     // if the column does not have a dataIndex mapping,
55157     // map it to the order it is in the config
55158     for(var i = 0, len = config.length; i < len; i++){
55159         var c = config[i];
55160         if(typeof c.dataIndex == "undefined"){
55161             c.dataIndex = i;
55162         }
55163         if(typeof c.renderer == "string"){
55164             c.renderer = Roo.util.Format[c.renderer];
55165         }
55166         if(typeof c.id == "undefined"){
55167             c.id = Roo.id();
55168         }
55169         if(c.editor && c.editor.xtype){
55170             c.editor  = Roo.factory(c.editor, Roo.grid);
55171         }
55172         if(c.editor && c.editor.isFormField){
55173             c.editor = new Roo.grid.GridEditor(c.editor);
55174         }
55175         this.lookup[c.id] = c;
55176     }
55177
55178     /**
55179      * The width of columns which have no width specified (defaults to 100)
55180      * @type Number
55181      */
55182     this.defaultWidth = 100;
55183
55184     /**
55185      * Default sortable of columns which have no sortable specified (defaults to false)
55186      * @type Boolean
55187      */
55188     this.defaultSortable = false;
55189
55190     this.addEvents({
55191         /**
55192              * @event widthchange
55193              * Fires when the width of a column changes.
55194              * @param {ColumnModel} this
55195              * @param {Number} columnIndex The column index
55196              * @param {Number} newWidth The new width
55197              */
55198             "widthchange": true,
55199         /**
55200              * @event headerchange
55201              * Fires when the text of a header changes.
55202              * @param {ColumnModel} this
55203              * @param {Number} columnIndex The column index
55204              * @param {Number} newText The new header text
55205              */
55206             "headerchange": true,
55207         /**
55208              * @event hiddenchange
55209              * Fires when a column is hidden or "unhidden".
55210              * @param {ColumnModel} this
55211              * @param {Number} columnIndex The column index
55212              * @param {Boolean} hidden true if hidden, false otherwise
55213              */
55214             "hiddenchange": true,
55215             /**
55216          * @event columnmoved
55217          * Fires when a column is moved.
55218          * @param {ColumnModel} this
55219          * @param {Number} oldIndex
55220          * @param {Number} newIndex
55221          */
55222         "columnmoved" : true,
55223         /**
55224          * @event columlockchange
55225          * Fires when a column's locked state is changed
55226          * @param {ColumnModel} this
55227          * @param {Number} colIndex
55228          * @param {Boolean} locked true if locked
55229          */
55230         "columnlockchange" : true
55231     });
55232     Roo.grid.ColumnModel.superclass.constructor.call(this);
55233 };
55234 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55235     /**
55236      * @cfg {String} header The header text to display in the Grid view.
55237      */
55238     /**
55239      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55240      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55241      * specified, the column's index is used as an index into the Record's data Array.
55242      */
55243     /**
55244      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55245      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55246      */
55247     /**
55248      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55249      * Defaults to the value of the {@link #defaultSortable} property.
55250      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55251      */
55252     /**
55253      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55254      */
55255     /**
55256      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55257      */
55258     /**
55259      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55260      */
55261     /**
55262      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55263      */
55264     /**
55265      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55266      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55267      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55268      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55269      */
55270        /**
55271      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55272      */
55273     /**
55274      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55275      */
55276     /**
55277      * @cfg {String} cursor (Optional)
55278      */
55279     /**
55280      * Returns the id of the column at the specified index.
55281      * @param {Number} index The column index
55282      * @return {String} the id
55283      */
55284     getColumnId : function(index){
55285         return this.config[index].id;
55286     },
55287
55288     /**
55289      * Returns the column for a specified id.
55290      * @param {String} id The column id
55291      * @return {Object} the column
55292      */
55293     getColumnById : function(id){
55294         return this.lookup[id];
55295     },
55296
55297     
55298     /**
55299      * Returns the column for a specified dataIndex.
55300      * @param {String} dataIndex The column dataIndex
55301      * @return {Object|Boolean} the column or false if not found
55302      */
55303     getColumnByDataIndex: function(dataIndex){
55304         var index = this.findColumnIndex(dataIndex);
55305         return index > -1 ? this.config[index] : false;
55306     },
55307     
55308     /**
55309      * Returns the index for a specified column id.
55310      * @param {String} id The column id
55311      * @return {Number} the index, or -1 if not found
55312      */
55313     getIndexById : function(id){
55314         for(var i = 0, len = this.config.length; i < len; i++){
55315             if(this.config[i].id == id){
55316                 return i;
55317             }
55318         }
55319         return -1;
55320     },
55321     
55322     /**
55323      * Returns the index for a specified column dataIndex.
55324      * @param {String} dataIndex The column dataIndex
55325      * @return {Number} the index, or -1 if not found
55326      */
55327     
55328     findColumnIndex : function(dataIndex){
55329         for(var i = 0, len = this.config.length; i < len; i++){
55330             if(this.config[i].dataIndex == dataIndex){
55331                 return i;
55332             }
55333         }
55334         return -1;
55335     },
55336     
55337     
55338     moveColumn : function(oldIndex, newIndex){
55339         var c = this.config[oldIndex];
55340         this.config.splice(oldIndex, 1);
55341         this.config.splice(newIndex, 0, c);
55342         this.dataMap = null;
55343         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55344     },
55345
55346     isLocked : function(colIndex){
55347         return this.config[colIndex].locked === true;
55348     },
55349
55350     setLocked : function(colIndex, value, suppressEvent){
55351         if(this.isLocked(colIndex) == value){
55352             return;
55353         }
55354         this.config[colIndex].locked = value;
55355         if(!suppressEvent){
55356             this.fireEvent("columnlockchange", this, colIndex, value);
55357         }
55358     },
55359
55360     getTotalLockedWidth : function(){
55361         var totalWidth = 0;
55362         for(var i = 0; i < this.config.length; i++){
55363             if(this.isLocked(i) && !this.isHidden(i)){
55364                 this.totalWidth += this.getColumnWidth(i);
55365             }
55366         }
55367         return totalWidth;
55368     },
55369
55370     getLockedCount : function(){
55371         for(var i = 0, len = this.config.length; i < len; i++){
55372             if(!this.isLocked(i)){
55373                 return i;
55374             }
55375         }
55376     },
55377
55378     /**
55379      * Returns the number of columns.
55380      * @return {Number}
55381      */
55382     getColumnCount : function(visibleOnly){
55383         if(visibleOnly === true){
55384             var c = 0;
55385             for(var i = 0, len = this.config.length; i < len; i++){
55386                 if(!this.isHidden(i)){
55387                     c++;
55388                 }
55389             }
55390             return c;
55391         }
55392         return this.config.length;
55393     },
55394
55395     /**
55396      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55397      * @param {Function} fn
55398      * @param {Object} scope (optional)
55399      * @return {Array} result
55400      */
55401     getColumnsBy : function(fn, scope){
55402         var r = [];
55403         for(var i = 0, len = this.config.length; i < len; i++){
55404             var c = this.config[i];
55405             if(fn.call(scope||this, c, i) === true){
55406                 r[r.length] = c;
55407             }
55408         }
55409         return r;
55410     },
55411
55412     /**
55413      * Returns true if the specified column is sortable.
55414      * @param {Number} col The column index
55415      * @return {Boolean}
55416      */
55417     isSortable : function(col){
55418         if(typeof this.config[col].sortable == "undefined"){
55419             return this.defaultSortable;
55420         }
55421         return this.config[col].sortable;
55422     },
55423
55424     /**
55425      * Returns the rendering (formatting) function defined for the column.
55426      * @param {Number} col The column index.
55427      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55428      */
55429     getRenderer : function(col){
55430         if(!this.config[col].renderer){
55431             return Roo.grid.ColumnModel.defaultRenderer;
55432         }
55433         return this.config[col].renderer;
55434     },
55435
55436     /**
55437      * Sets the rendering (formatting) function for a column.
55438      * @param {Number} col The column index
55439      * @param {Function} fn The function to use to process the cell's raw data
55440      * to return HTML markup for the grid view. The render function is called with
55441      * the following parameters:<ul>
55442      * <li>Data value.</li>
55443      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55444      * <li>css A CSS style string to apply to the table cell.</li>
55445      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55446      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55447      * <li>Row index</li>
55448      * <li>Column index</li>
55449      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55450      */
55451     setRenderer : function(col, fn){
55452         this.config[col].renderer = fn;
55453     },
55454
55455     /**
55456      * Returns the width for the specified column.
55457      * @param {Number} col The column index
55458      * @return {Number}
55459      */
55460     getColumnWidth : function(col){
55461         return this.config[col].width * 1 || this.defaultWidth;
55462     },
55463
55464     /**
55465      * Sets the width for a column.
55466      * @param {Number} col The column index
55467      * @param {Number} width The new width
55468      */
55469     setColumnWidth : function(col, width, suppressEvent){
55470         this.config[col].width = width;
55471         this.totalWidth = null;
55472         if(!suppressEvent){
55473              this.fireEvent("widthchange", this, col, width);
55474         }
55475     },
55476
55477     /**
55478      * Returns the total width of all columns.
55479      * @param {Boolean} includeHidden True to include hidden column widths
55480      * @return {Number}
55481      */
55482     getTotalWidth : function(includeHidden){
55483         if(!this.totalWidth){
55484             this.totalWidth = 0;
55485             for(var i = 0, len = this.config.length; i < len; i++){
55486                 if(includeHidden || !this.isHidden(i)){
55487                     this.totalWidth += this.getColumnWidth(i);
55488                 }
55489             }
55490         }
55491         return this.totalWidth;
55492     },
55493
55494     /**
55495      * Returns the header for the specified column.
55496      * @param {Number} col The column index
55497      * @return {String}
55498      */
55499     getColumnHeader : function(col){
55500         return this.config[col].header;
55501     },
55502
55503     /**
55504      * Sets the header for a column.
55505      * @param {Number} col The column index
55506      * @param {String} header The new header
55507      */
55508     setColumnHeader : function(col, header){
55509         this.config[col].header = header;
55510         this.fireEvent("headerchange", this, col, header);
55511     },
55512
55513     /**
55514      * Returns the tooltip for the specified column.
55515      * @param {Number} col The column index
55516      * @return {String}
55517      */
55518     getColumnTooltip : function(col){
55519             return this.config[col].tooltip;
55520     },
55521     /**
55522      * Sets the tooltip for a column.
55523      * @param {Number} col The column index
55524      * @param {String} tooltip The new tooltip
55525      */
55526     setColumnTooltip : function(col, tooltip){
55527             this.config[col].tooltip = tooltip;
55528     },
55529
55530     /**
55531      * Returns the dataIndex for the specified column.
55532      * @param {Number} col The column index
55533      * @return {Number}
55534      */
55535     getDataIndex : function(col){
55536         return this.config[col].dataIndex;
55537     },
55538
55539     /**
55540      * Sets the dataIndex for a column.
55541      * @param {Number} col The column index
55542      * @param {Number} dataIndex The new dataIndex
55543      */
55544     setDataIndex : function(col, dataIndex){
55545         this.config[col].dataIndex = dataIndex;
55546     },
55547
55548     
55549     
55550     /**
55551      * Returns true if the cell is editable.
55552      * @param {Number} colIndex The column index
55553      * @param {Number} rowIndex The row index
55554      * @return {Boolean}
55555      */
55556     isCellEditable : function(colIndex, rowIndex){
55557         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55558     },
55559
55560     /**
55561      * Returns the editor defined for the cell/column.
55562      * return false or null to disable editing.
55563      * @param {Number} colIndex The column index
55564      * @param {Number} rowIndex The row index
55565      * @return {Object}
55566      */
55567     getCellEditor : function(colIndex, rowIndex){
55568         return this.config[colIndex].editor;
55569     },
55570
55571     /**
55572      * Sets if a column is editable.
55573      * @param {Number} col The column index
55574      * @param {Boolean} editable True if the column is editable
55575      */
55576     setEditable : function(col, editable){
55577         this.config[col].editable = editable;
55578     },
55579
55580
55581     /**
55582      * Returns true if the column is hidden.
55583      * @param {Number} colIndex The column index
55584      * @return {Boolean}
55585      */
55586     isHidden : function(colIndex){
55587         return this.config[colIndex].hidden;
55588     },
55589
55590
55591     /**
55592      * Returns true if the column width cannot be changed
55593      */
55594     isFixed : function(colIndex){
55595         return this.config[colIndex].fixed;
55596     },
55597
55598     /**
55599      * Returns true if the column can be resized
55600      * @return {Boolean}
55601      */
55602     isResizable : function(colIndex){
55603         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55604     },
55605     /**
55606      * Sets if a column is hidden.
55607      * @param {Number} colIndex The column index
55608      * @param {Boolean} hidden True if the column is hidden
55609      */
55610     setHidden : function(colIndex, hidden){
55611         this.config[colIndex].hidden = hidden;
55612         this.totalWidth = null;
55613         this.fireEvent("hiddenchange", this, colIndex, hidden);
55614     },
55615
55616     /**
55617      * Sets the editor for a column.
55618      * @param {Number} col The column index
55619      * @param {Object} editor The editor object
55620      */
55621     setEditor : function(col, editor){
55622         this.config[col].editor = editor;
55623     }
55624 });
55625
55626 Roo.grid.ColumnModel.defaultRenderer = function(value){
55627         if(typeof value == "string" && value.length < 1){
55628             return "&#160;";
55629         }
55630         return value;
55631 };
55632
55633 // Alias for backwards compatibility
55634 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55635 /*
55636  * Based on:
55637  * Ext JS Library 1.1.1
55638  * Copyright(c) 2006-2007, Ext JS, LLC.
55639  *
55640  * Originally Released Under LGPL - original licence link has changed is not relivant.
55641  *
55642  * Fork - LGPL
55643  * <script type="text/javascript">
55644  */
55645
55646 /**
55647  * @class Roo.grid.AbstractSelectionModel
55648  * @extends Roo.util.Observable
55649  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55650  * implemented by descendant classes.  This class should not be directly instantiated.
55651  * @constructor
55652  */
55653 Roo.grid.AbstractSelectionModel = function(){
55654     this.locked = false;
55655     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55656 };
55657
55658 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55659     /** @ignore Called by the grid automatically. Do not call directly. */
55660     init : function(grid){
55661         this.grid = grid;
55662         this.initEvents();
55663     },
55664
55665     /**
55666      * Locks the selections.
55667      */
55668     lock : function(){
55669         this.locked = true;
55670     },
55671
55672     /**
55673      * Unlocks the selections.
55674      */
55675     unlock : function(){
55676         this.locked = false;
55677     },
55678
55679     /**
55680      * Returns true if the selections are locked.
55681      * @return {Boolean}
55682      */
55683     isLocked : function(){
55684         return this.locked;
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  * @extends Roo.grid.AbstractSelectionModel
55698  * @class Roo.grid.RowSelectionModel
55699  * The default SelectionModel used by {@link Roo.grid.Grid}.
55700  * It supports multiple selections and keyboard selection/navigation. 
55701  * @constructor
55702  * @param {Object} config
55703  */
55704 Roo.grid.RowSelectionModel = function(config){
55705     Roo.apply(this, config);
55706     this.selections = new Roo.util.MixedCollection(false, function(o){
55707         return o.id;
55708     });
55709
55710     this.last = false;
55711     this.lastActive = false;
55712
55713     this.addEvents({
55714         /**
55715              * @event selectionchange
55716              * Fires when the selection changes
55717              * @param {SelectionModel} this
55718              */
55719             "selectionchange" : true,
55720         /**
55721              * @event afterselectionchange
55722              * Fires after the selection changes (eg. by key press or clicking)
55723              * @param {SelectionModel} this
55724              */
55725             "afterselectionchange" : true,
55726         /**
55727              * @event beforerowselect
55728              * Fires when a row is selected being selected, return false to cancel.
55729              * @param {SelectionModel} this
55730              * @param {Number} rowIndex The selected index
55731              * @param {Boolean} keepExisting False if other selections will be cleared
55732              */
55733             "beforerowselect" : true,
55734         /**
55735              * @event rowselect
55736              * Fires when a row is selected.
55737              * @param {SelectionModel} this
55738              * @param {Number} rowIndex The selected index
55739              * @param {Roo.data.Record} r The record
55740              */
55741             "rowselect" : true,
55742         /**
55743              * @event rowdeselect
55744              * Fires when a row is deselected.
55745              * @param {SelectionModel} this
55746              * @param {Number} rowIndex The selected index
55747              */
55748         "rowdeselect" : true
55749     });
55750     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55751     this.locked = false;
55752 };
55753
55754 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55755     /**
55756      * @cfg {Boolean} singleSelect
55757      * True to allow selection of only one row at a time (defaults to false)
55758      */
55759     singleSelect : false,
55760
55761     // private
55762     initEvents : function(){
55763
55764         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55765             this.grid.on("mousedown", this.handleMouseDown, this);
55766         }else{ // allow click to work like normal
55767             this.grid.on("rowclick", this.handleDragableRowClick, this);
55768         }
55769
55770         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55771             "up" : function(e){
55772                 if(!e.shiftKey){
55773                     this.selectPrevious(e.shiftKey);
55774                 }else if(this.last !== false && this.lastActive !== false){
55775                     var last = this.last;
55776                     this.selectRange(this.last,  this.lastActive-1);
55777                     this.grid.getView().focusRow(this.lastActive);
55778                     if(last !== false){
55779                         this.last = last;
55780                     }
55781                 }else{
55782                     this.selectFirstRow();
55783                 }
55784                 this.fireEvent("afterselectionchange", this);
55785             },
55786             "down" : function(e){
55787                 if(!e.shiftKey){
55788                     this.selectNext(e.shiftKey);
55789                 }else if(this.last !== false && this.lastActive !== false){
55790                     var last = this.last;
55791                     this.selectRange(this.last,  this.lastActive+1);
55792                     this.grid.getView().focusRow(this.lastActive);
55793                     if(last !== false){
55794                         this.last = last;
55795                     }
55796                 }else{
55797                     this.selectFirstRow();
55798                 }
55799                 this.fireEvent("afterselectionchange", this);
55800             },
55801             scope: this
55802         });
55803
55804         var view = this.grid.view;
55805         view.on("refresh", this.onRefresh, this);
55806         view.on("rowupdated", this.onRowUpdated, this);
55807         view.on("rowremoved", this.onRemove, this);
55808     },
55809
55810     // private
55811     onRefresh : function(){
55812         var ds = this.grid.dataSource, i, v = this.grid.view;
55813         var s = this.selections;
55814         s.each(function(r){
55815             if((i = ds.indexOfId(r.id)) != -1){
55816                 v.onRowSelect(i);
55817             }else{
55818                 s.remove(r);
55819             }
55820         });
55821     },
55822
55823     // private
55824     onRemove : function(v, index, r){
55825         this.selections.remove(r);
55826     },
55827
55828     // private
55829     onRowUpdated : function(v, index, r){
55830         if(this.isSelected(r)){
55831             v.onRowSelect(index);
55832         }
55833     },
55834
55835     /**
55836      * Select records.
55837      * @param {Array} records The records to select
55838      * @param {Boolean} keepExisting (optional) True to keep existing selections
55839      */
55840     selectRecords : function(records, keepExisting){
55841         if(!keepExisting){
55842             this.clearSelections();
55843         }
55844         var ds = this.grid.dataSource;
55845         for(var i = 0, len = records.length; i < len; i++){
55846             this.selectRow(ds.indexOf(records[i]), true);
55847         }
55848     },
55849
55850     /**
55851      * Gets the number of selected rows.
55852      * @return {Number}
55853      */
55854     getCount : function(){
55855         return this.selections.length;
55856     },
55857
55858     /**
55859      * Selects the first row in the grid.
55860      */
55861     selectFirstRow : function(){
55862         this.selectRow(0);
55863     },
55864
55865     /**
55866      * Select the last row.
55867      * @param {Boolean} keepExisting (optional) True to keep existing selections
55868      */
55869     selectLastRow : function(keepExisting){
55870         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55871     },
55872
55873     /**
55874      * Selects the row immediately following the last selected row.
55875      * @param {Boolean} keepExisting (optional) True to keep existing selections
55876      */
55877     selectNext : function(keepExisting){
55878         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55879             this.selectRow(this.last+1, keepExisting);
55880             this.grid.getView().focusRow(this.last);
55881         }
55882     },
55883
55884     /**
55885      * Selects the row that precedes the last selected row.
55886      * @param {Boolean} keepExisting (optional) True to keep existing selections
55887      */
55888     selectPrevious : function(keepExisting){
55889         if(this.last){
55890             this.selectRow(this.last-1, keepExisting);
55891             this.grid.getView().focusRow(this.last);
55892         }
55893     },
55894
55895     /**
55896      * Returns the selected records
55897      * @return {Array} Array of selected records
55898      */
55899     getSelections : function(){
55900         return [].concat(this.selections.items);
55901     },
55902
55903     /**
55904      * Returns the first selected record.
55905      * @return {Record}
55906      */
55907     getSelected : function(){
55908         return this.selections.itemAt(0);
55909     },
55910
55911
55912     /**
55913      * Clears all selections.
55914      */
55915     clearSelections : function(fast){
55916         if(this.locked) return;
55917         if(fast !== true){
55918             var ds = this.grid.dataSource;
55919             var s = this.selections;
55920             s.each(function(r){
55921                 this.deselectRow(ds.indexOfId(r.id));
55922             }, this);
55923             s.clear();
55924         }else{
55925             this.selections.clear();
55926         }
55927         this.last = false;
55928     },
55929
55930
55931     /**
55932      * Selects all rows.
55933      */
55934     selectAll : function(){
55935         if(this.locked) return;
55936         this.selections.clear();
55937         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55938             this.selectRow(i, true);
55939         }
55940     },
55941
55942     /**
55943      * Returns True if there is a selection.
55944      * @return {Boolean}
55945      */
55946     hasSelection : function(){
55947         return this.selections.length > 0;
55948     },
55949
55950     /**
55951      * Returns True if the specified row is selected.
55952      * @param {Number/Record} record The record or index of the record to check
55953      * @return {Boolean}
55954      */
55955     isSelected : function(index){
55956         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55957         return (r && this.selections.key(r.id) ? true : false);
55958     },
55959
55960     /**
55961      * Returns True if the specified record id is selected.
55962      * @param {String} id The id of record to check
55963      * @return {Boolean}
55964      */
55965     isIdSelected : function(id){
55966         return (this.selections.key(id) ? true : false);
55967     },
55968
55969     // private
55970     handleMouseDown : function(e, t){
55971         var view = this.grid.getView(), rowIndex;
55972         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55973             return;
55974         };
55975         if(e.shiftKey && this.last !== false){
55976             var last = this.last;
55977             this.selectRange(last, rowIndex, e.ctrlKey);
55978             this.last = last; // reset the last
55979             view.focusRow(rowIndex);
55980         }else{
55981             var isSelected = this.isSelected(rowIndex);
55982             if(e.button !== 0 && isSelected){
55983                 view.focusRow(rowIndex);
55984             }else if(e.ctrlKey && isSelected){
55985                 this.deselectRow(rowIndex);
55986             }else if(!isSelected){
55987                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
55988                 view.focusRow(rowIndex);
55989             }
55990         }
55991         this.fireEvent("afterselectionchange", this);
55992     },
55993     // private
55994     handleDragableRowClick :  function(grid, rowIndex, e) 
55995     {
55996         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
55997             this.selectRow(rowIndex, false);
55998             grid.view.focusRow(rowIndex);
55999              this.fireEvent("afterselectionchange", this);
56000         }
56001     },
56002     
56003     /**
56004      * Selects multiple rows.
56005      * @param {Array} rows Array of the indexes of the row to select
56006      * @param {Boolean} keepExisting (optional) True to keep existing selections
56007      */
56008     selectRows : function(rows, keepExisting){
56009         if(!keepExisting){
56010             this.clearSelections();
56011         }
56012         for(var i = 0, len = rows.length; i < len; i++){
56013             this.selectRow(rows[i], true);
56014         }
56015     },
56016
56017     /**
56018      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56019      * @param {Number} startRow The index of the first row in the range
56020      * @param {Number} endRow The index of the last row in the range
56021      * @param {Boolean} keepExisting (optional) True to retain existing selections
56022      */
56023     selectRange : function(startRow, endRow, keepExisting){
56024         if(this.locked) return;
56025         if(!keepExisting){
56026             this.clearSelections();
56027         }
56028         if(startRow <= endRow){
56029             for(var i = startRow; i <= endRow; i++){
56030                 this.selectRow(i, true);
56031             }
56032         }else{
56033             for(var i = startRow; i >= endRow; i--){
56034                 this.selectRow(i, true);
56035             }
56036         }
56037     },
56038
56039     /**
56040      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56041      * @param {Number} startRow The index of the first row in the range
56042      * @param {Number} endRow The index of the last row in the range
56043      */
56044     deselectRange : function(startRow, endRow, preventViewNotify){
56045         if(this.locked) return;
56046         for(var i = startRow; i <= endRow; i++){
56047             this.deselectRow(i, preventViewNotify);
56048         }
56049     },
56050
56051     /**
56052      * Selects a row.
56053      * @param {Number} row The index of the row to select
56054      * @param {Boolean} keepExisting (optional) True to keep existing selections
56055      */
56056     selectRow : function(index, keepExisting, preventViewNotify){
56057         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
56058         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56059             if(!keepExisting || this.singleSelect){
56060                 this.clearSelections();
56061             }
56062             var r = this.grid.dataSource.getAt(index);
56063             this.selections.add(r);
56064             this.last = this.lastActive = index;
56065             if(!preventViewNotify){
56066                 this.grid.getView().onRowSelect(index);
56067             }
56068             this.fireEvent("rowselect", this, index, r);
56069             this.fireEvent("selectionchange", this);
56070         }
56071     },
56072
56073     /**
56074      * Deselects a row.
56075      * @param {Number} row The index of the row to deselect
56076      */
56077     deselectRow : function(index, preventViewNotify){
56078         if(this.locked) return;
56079         if(this.last == index){
56080             this.last = false;
56081         }
56082         if(this.lastActive == index){
56083             this.lastActive = false;
56084         }
56085         var r = this.grid.dataSource.getAt(index);
56086         this.selections.remove(r);
56087         if(!preventViewNotify){
56088             this.grid.getView().onRowDeselect(index);
56089         }
56090         this.fireEvent("rowdeselect", this, index);
56091         this.fireEvent("selectionchange", this);
56092     },
56093
56094     // private
56095     restoreLast : function(){
56096         if(this._last){
56097             this.last = this._last;
56098         }
56099     },
56100
56101     // private
56102     acceptsNav : function(row, col, cm){
56103         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56104     },
56105
56106     // private
56107     onEditorKey : function(field, e){
56108         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56109         if(k == e.TAB){
56110             e.stopEvent();
56111             ed.completeEdit();
56112             if(e.shiftKey){
56113                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56114             }else{
56115                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56116             }
56117         }else if(k == e.ENTER && !e.ctrlKey){
56118             e.stopEvent();
56119             ed.completeEdit();
56120             if(e.shiftKey){
56121                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56122             }else{
56123                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56124             }
56125         }else if(k == e.ESC){
56126             ed.cancelEdit();
56127         }
56128         if(newCell){
56129             g.startEditing(newCell[0], newCell[1]);
56130         }
56131     }
56132 });/*
56133  * Based on:
56134  * Ext JS Library 1.1.1
56135  * Copyright(c) 2006-2007, Ext JS, LLC.
56136  *
56137  * Originally Released Under LGPL - original licence link has changed is not relivant.
56138  *
56139  * Fork - LGPL
56140  * <script type="text/javascript">
56141  */
56142 /**
56143  * @class Roo.grid.CellSelectionModel
56144  * @extends Roo.grid.AbstractSelectionModel
56145  * This class provides the basic implementation for cell selection in a grid.
56146  * @constructor
56147  * @param {Object} config The object containing the configuration of this model.
56148  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56149  */
56150 Roo.grid.CellSelectionModel = function(config){
56151     Roo.apply(this, config);
56152
56153     this.selection = null;
56154
56155     this.addEvents({
56156         /**
56157              * @event beforerowselect
56158              * Fires before a cell is selected.
56159              * @param {SelectionModel} this
56160              * @param {Number} rowIndex The selected row index
56161              * @param {Number} colIndex The selected cell index
56162              */
56163             "beforecellselect" : true,
56164         /**
56165              * @event cellselect
56166              * Fires when a cell is selected.
56167              * @param {SelectionModel} this
56168              * @param {Number} rowIndex The selected row index
56169              * @param {Number} colIndex The selected cell index
56170              */
56171             "cellselect" : true,
56172         /**
56173              * @event selectionchange
56174              * Fires when the active selection changes.
56175              * @param {SelectionModel} this
56176              * @param {Object} selection null for no selection or an object (o) with two properties
56177                 <ul>
56178                 <li>o.record: the record object for the row the selection is in</li>
56179                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56180                 </ul>
56181              */
56182             "selectionchange" : true,
56183         /**
56184              * @event tabend
56185              * Fires when the tab (or enter) was pressed on the last editable cell
56186              * You can use this to trigger add new row.
56187              * @param {SelectionModel} this
56188              */
56189             "tabend" : true,
56190          /**
56191              * @event beforeeditnext
56192              * Fires before the next editable sell is made active
56193              * You can use this to skip to another cell or fire the tabend
56194              *    if you set cell to false
56195              * @param {Object} eventdata object : { cell : [ row, col ] } 
56196              */
56197             "beforeeditnext" : true
56198     });
56199     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56200 };
56201
56202 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56203     
56204     enter_is_tab: false,
56205
56206     /** @ignore */
56207     initEvents : function(){
56208         this.grid.on("mousedown", this.handleMouseDown, this);
56209         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56210         var view = this.grid.view;
56211         view.on("refresh", this.onViewChange, this);
56212         view.on("rowupdated", this.onRowUpdated, this);
56213         view.on("beforerowremoved", this.clearSelections, this);
56214         view.on("beforerowsinserted", this.clearSelections, this);
56215         if(this.grid.isEditor){
56216             this.grid.on("beforeedit", this.beforeEdit,  this);
56217         }
56218     },
56219
56220         //private
56221     beforeEdit : function(e){
56222         this.select(e.row, e.column, false, true, e.record);
56223     },
56224
56225         //private
56226     onRowUpdated : function(v, index, r){
56227         if(this.selection && this.selection.record == r){
56228             v.onCellSelect(index, this.selection.cell[1]);
56229         }
56230     },
56231
56232         //private
56233     onViewChange : function(){
56234         this.clearSelections(true);
56235     },
56236
56237         /**
56238          * Returns the currently selected cell,.
56239          * @return {Array} The selected cell (row, column) or null if none selected.
56240          */
56241     getSelectedCell : function(){
56242         return this.selection ? this.selection.cell : null;
56243     },
56244
56245     /**
56246      * Clears all selections.
56247      * @param {Boolean} true to prevent the gridview from being notified about the change.
56248      */
56249     clearSelections : function(preventNotify){
56250         var s = this.selection;
56251         if(s){
56252             if(preventNotify !== true){
56253                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56254             }
56255             this.selection = null;
56256             this.fireEvent("selectionchange", this, null);
56257         }
56258     },
56259
56260     /**
56261      * Returns true if there is a selection.
56262      * @return {Boolean}
56263      */
56264     hasSelection : function(){
56265         return this.selection ? true : false;
56266     },
56267
56268     /** @ignore */
56269     handleMouseDown : function(e, t){
56270         var v = this.grid.getView();
56271         if(this.isLocked()){
56272             return;
56273         };
56274         var row = v.findRowIndex(t);
56275         var cell = v.findCellIndex(t);
56276         if(row !== false && cell !== false){
56277             this.select(row, cell);
56278         }
56279     },
56280
56281     /**
56282      * Selects a cell.
56283      * @param {Number} rowIndex
56284      * @param {Number} collIndex
56285      */
56286     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56287         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56288             this.clearSelections();
56289             r = r || this.grid.dataSource.getAt(rowIndex);
56290             this.selection = {
56291                 record : r,
56292                 cell : [rowIndex, colIndex]
56293             };
56294             if(!preventViewNotify){
56295                 var v = this.grid.getView();
56296                 v.onCellSelect(rowIndex, colIndex);
56297                 if(preventFocus !== true){
56298                     v.focusCell(rowIndex, colIndex);
56299                 }
56300             }
56301             this.fireEvent("cellselect", this, rowIndex, colIndex);
56302             this.fireEvent("selectionchange", this, this.selection);
56303         }
56304     },
56305
56306         //private
56307     isSelectable : function(rowIndex, colIndex, cm){
56308         return !cm.isHidden(colIndex);
56309     },
56310
56311     /** @ignore */
56312     handleKeyDown : function(e){
56313         //Roo.log('Cell Sel Model handleKeyDown');
56314         if(!e.isNavKeyPress()){
56315             return;
56316         }
56317         var g = this.grid, s = this.selection;
56318         if(!s){
56319             e.stopEvent();
56320             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56321             if(cell){
56322                 this.select(cell[0], cell[1]);
56323             }
56324             return;
56325         }
56326         var sm = this;
56327         var walk = function(row, col, step){
56328             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56329         };
56330         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56331         var newCell;
56332
56333       
56334
56335         switch(k){
56336             case e.TAB:
56337                 // handled by onEditorKey
56338                 if (g.isEditor && g.editing) {
56339                     return;
56340                 }
56341                 if(e.shiftKey) {
56342                     newCell = walk(r, c-1, -1);
56343                 } else {
56344                     newCell = walk(r, c+1, 1);
56345                 }
56346                 break;
56347             
56348             case e.DOWN:
56349                newCell = walk(r+1, c, 1);
56350                 break;
56351             
56352             case e.UP:
56353                 newCell = walk(r-1, c, -1);
56354                 break;
56355             
56356             case e.RIGHT:
56357                 newCell = walk(r, c+1, 1);
56358                 break;
56359             
56360             case e.LEFT:
56361                 newCell = walk(r, c-1, -1);
56362                 break;
56363             
56364             case e.ENTER:
56365                 
56366                 if(g.isEditor && !g.editing){
56367                    g.startEditing(r, c);
56368                    e.stopEvent();
56369                    return;
56370                 }
56371                 
56372                 
56373              break;
56374         };
56375         if(newCell){
56376             this.select(newCell[0], newCell[1]);
56377             e.stopEvent();
56378             
56379         }
56380     },
56381
56382     acceptsNav : function(row, col, cm){
56383         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56384     },
56385     /**
56386      * Selects a cell.
56387      * @param {Number} field (not used) - as it's normally used as a listener
56388      * @param {Number} e - event - fake it by using
56389      *
56390      * var e = Roo.EventObjectImpl.prototype;
56391      * e.keyCode = e.TAB
56392      *
56393      * 
56394      */
56395     onEditorKey : function(field, e){
56396         
56397         var k = e.getKey(),
56398             newCell,
56399             g = this.grid,
56400             ed = g.activeEditor,
56401             forward = false;
56402         ///Roo.log('onEditorKey' + k);
56403         
56404         
56405         if (this.enter_is_tab && k == e.ENTER) {
56406             k = e.TAB;
56407         }
56408         
56409         if(k == e.TAB){
56410             if(e.shiftKey){
56411                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56412             }else{
56413                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56414                 forward = true;
56415             }
56416             
56417             e.stopEvent();
56418             
56419         } else if(k == e.ENTER &&  !e.ctrlKey){
56420             ed.completeEdit();
56421             e.stopEvent();
56422             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56423         
56424                 } else if(k == e.ESC){
56425             ed.cancelEdit();
56426         }
56427                 
56428         if (newCell) {
56429             var ecall = { cell : newCell, forward : forward };
56430             this.fireEvent('beforeeditnext', ecall );
56431             newCell = ecall.cell;
56432                         forward = ecall.forward;
56433         }
56434                 
56435         if(newCell){
56436             //Roo.log('next cell after edit');
56437             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56438         } else if (forward) {
56439             // tabbed past last
56440             this.fireEvent.defer(100, this, ['tabend',this]);
56441         }
56442     }
56443 });/*
56444  * Based on:
56445  * Ext JS Library 1.1.1
56446  * Copyright(c) 2006-2007, Ext JS, LLC.
56447  *
56448  * Originally Released Under LGPL - original licence link has changed is not relivant.
56449  *
56450  * Fork - LGPL
56451  * <script type="text/javascript">
56452  */
56453  
56454 /**
56455  * @class Roo.grid.EditorGrid
56456  * @extends Roo.grid.Grid
56457  * Class for creating and editable grid.
56458  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56459  * The container MUST have some type of size defined for the grid to fill. The container will be 
56460  * automatically set to position relative if it isn't already.
56461  * @param {Object} dataSource The data model to bind to
56462  * @param {Object} colModel The column model with info about this grid's columns
56463  */
56464 Roo.grid.EditorGrid = function(container, config){
56465     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56466     this.getGridEl().addClass("xedit-grid");
56467
56468     if(!this.selModel){
56469         this.selModel = new Roo.grid.CellSelectionModel();
56470     }
56471
56472     this.activeEditor = null;
56473
56474         this.addEvents({
56475             /**
56476              * @event beforeedit
56477              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56478              * <ul style="padding:5px;padding-left:16px;">
56479              * <li>grid - This grid</li>
56480              * <li>record - The record being edited</li>
56481              * <li>field - The field name being edited</li>
56482              * <li>value - The value for the field being edited.</li>
56483              * <li>row - The grid row index</li>
56484              * <li>column - The grid column index</li>
56485              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56486              * </ul>
56487              * @param {Object} e An edit event (see above for description)
56488              */
56489             "beforeedit" : true,
56490             /**
56491              * @event afteredit
56492              * Fires after a cell is edited. <br />
56493              * <ul style="padding:5px;padding-left:16px;">
56494              * <li>grid - This grid</li>
56495              * <li>record - The record being edited</li>
56496              * <li>field - The field name being edited</li>
56497              * <li>value - The value being set</li>
56498              * <li>originalValue - The original value for the field, before the edit.</li>
56499              * <li>row - The grid row index</li>
56500              * <li>column - The grid column index</li>
56501              * </ul>
56502              * @param {Object} e An edit event (see above for description)
56503              */
56504             "afteredit" : true,
56505             /**
56506              * @event validateedit
56507              * Fires after a cell is edited, but before the value is set in the record. 
56508          * You can use this to modify the value being set in the field, Return false
56509              * to cancel the change. The edit event object has the following properties <br />
56510              * <ul style="padding:5px;padding-left:16px;">
56511          * <li>editor - This editor</li>
56512              * <li>grid - This grid</li>
56513              * <li>record - The record being edited</li>
56514              * <li>field - The field name being edited</li>
56515              * <li>value - The value being set</li>
56516              * <li>originalValue - The original value for the field, before the edit.</li>
56517              * <li>row - The grid row index</li>
56518              * <li>column - The grid column index</li>
56519              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56520              * </ul>
56521              * @param {Object} e An edit event (see above for description)
56522              */
56523             "validateedit" : true
56524         });
56525     this.on("bodyscroll", this.stopEditing,  this);
56526     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56527 };
56528
56529 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56530     /**
56531      * @cfg {Number} clicksToEdit
56532      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56533      */
56534     clicksToEdit: 2,
56535
56536     // private
56537     isEditor : true,
56538     // private
56539     trackMouseOver: false, // causes very odd FF errors
56540
56541     onCellDblClick : function(g, row, col){
56542         this.startEditing(row, col);
56543     },
56544
56545     onEditComplete : function(ed, value, startValue){
56546         this.editing = false;
56547         this.activeEditor = null;
56548         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56549         var r = ed.record;
56550         var field = this.colModel.getDataIndex(ed.col);
56551         var e = {
56552             grid: this,
56553             record: r,
56554             field: field,
56555             originalValue: startValue,
56556             value: value,
56557             row: ed.row,
56558             column: ed.col,
56559             cancel:false,
56560             editor: ed
56561         };
56562         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56563         cell.show();
56564           
56565         if(String(value) !== String(startValue)){
56566             
56567             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56568                 r.set(field, e.value);
56569                 // if we are dealing with a combo box..
56570                 // then we also set the 'name' colum to be the displayField
56571                 if (ed.field.displayField && ed.field.name) {
56572                     r.set(ed.field.name, ed.field.el.dom.value);
56573                 }
56574                 
56575                 delete e.cancel; //?? why!!!
56576                 this.fireEvent("afteredit", e);
56577             }
56578         } else {
56579             this.fireEvent("afteredit", e); // always fire it!
56580         }
56581         this.view.focusCell(ed.row, ed.col);
56582     },
56583
56584     /**
56585      * Starts editing the specified for the specified row/column
56586      * @param {Number} rowIndex
56587      * @param {Number} colIndex
56588      */
56589     startEditing : function(row, col){
56590         this.stopEditing();
56591         if(this.colModel.isCellEditable(col, row)){
56592             this.view.ensureVisible(row, col, true);
56593           
56594             var r = this.dataSource.getAt(row);
56595             var field = this.colModel.getDataIndex(col);
56596             var cell = Roo.get(this.view.getCell(row,col));
56597             var e = {
56598                 grid: this,
56599                 record: r,
56600                 field: field,
56601                 value: r.data[field],
56602                 row: row,
56603                 column: col,
56604                 cancel:false 
56605             };
56606             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56607                 this.editing = true;
56608                 var ed = this.colModel.getCellEditor(col, row);
56609                 
56610                 if (!ed) {
56611                     return;
56612                 }
56613                 if(!ed.rendered){
56614                     ed.render(ed.parentEl || document.body);
56615                 }
56616                 ed.field.reset();
56617                
56618                 cell.hide();
56619                 
56620                 (function(){ // complex but required for focus issues in safari, ie and opera
56621                     ed.row = row;
56622                     ed.col = col;
56623                     ed.record = r;
56624                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56625                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56626                     this.activeEditor = ed;
56627                     var v = r.data[field];
56628                     ed.startEdit(this.view.getCell(row, col), v);
56629                     // combo's with 'displayField and name set
56630                     if (ed.field.displayField && ed.field.name) {
56631                         ed.field.el.dom.value = r.data[ed.field.name];
56632                     }
56633                     
56634                     
56635                 }).defer(50, this);
56636             }
56637         }
56638     },
56639         
56640     /**
56641      * Stops any active editing
56642      */
56643     stopEditing : function(){
56644         if(this.activeEditor){
56645             this.activeEditor.completeEdit();
56646         }
56647         this.activeEditor = null;
56648     },
56649         
56650          /**
56651      * Called to get grid's drag proxy text, by default returns this.ddText.
56652      * @return {String}
56653      */
56654     getDragDropText : function(){
56655         var count = this.selModel.getSelectedCell() ? 1 : 0;
56656         return String.format(this.ddText, count, count == 1 ? '' : 's');
56657     }
56658         
56659 });/*
56660  * Based on:
56661  * Ext JS Library 1.1.1
56662  * Copyright(c) 2006-2007, Ext JS, LLC.
56663  *
56664  * Originally Released Under LGPL - original licence link has changed is not relivant.
56665  *
56666  * Fork - LGPL
56667  * <script type="text/javascript">
56668  */
56669
56670 // private - not really -- you end up using it !
56671 // This is a support class used internally by the Grid components
56672
56673 /**
56674  * @class Roo.grid.GridEditor
56675  * @extends Roo.Editor
56676  * Class for creating and editable grid elements.
56677  * @param {Object} config any settings (must include field)
56678  */
56679 Roo.grid.GridEditor = function(field, config){
56680     if (!config && field.field) {
56681         config = field;
56682         field = Roo.factory(config.field, Roo.form);
56683     }
56684     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56685     field.monitorTab = false;
56686 };
56687
56688 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56689     
56690     /**
56691      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56692      */
56693     
56694     alignment: "tl-tl",
56695     autoSize: "width",
56696     hideEl : false,
56697     cls: "x-small-editor x-grid-editor",
56698     shim:false,
56699     shadow:"frame"
56700 });/*
56701  * Based on:
56702  * Ext JS Library 1.1.1
56703  * Copyright(c) 2006-2007, Ext JS, LLC.
56704  *
56705  * Originally Released Under LGPL - original licence link has changed is not relivant.
56706  *
56707  * Fork - LGPL
56708  * <script type="text/javascript">
56709  */
56710   
56711
56712   
56713 Roo.grid.PropertyRecord = Roo.data.Record.create([
56714     {name:'name',type:'string'},  'value'
56715 ]);
56716
56717
56718 Roo.grid.PropertyStore = function(grid, source){
56719     this.grid = grid;
56720     this.store = new Roo.data.Store({
56721         recordType : Roo.grid.PropertyRecord
56722     });
56723     this.store.on('update', this.onUpdate,  this);
56724     if(source){
56725         this.setSource(source);
56726     }
56727     Roo.grid.PropertyStore.superclass.constructor.call(this);
56728 };
56729
56730
56731
56732 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56733     setSource : function(o){
56734         this.source = o;
56735         this.store.removeAll();
56736         var data = [];
56737         for(var k in o){
56738             if(this.isEditableValue(o[k])){
56739                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56740             }
56741         }
56742         this.store.loadRecords({records: data}, {}, true);
56743     },
56744
56745     onUpdate : function(ds, record, type){
56746         if(type == Roo.data.Record.EDIT){
56747             var v = record.data['value'];
56748             var oldValue = record.modified['value'];
56749             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56750                 this.source[record.id] = v;
56751                 record.commit();
56752                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56753             }else{
56754                 record.reject();
56755             }
56756         }
56757     },
56758
56759     getProperty : function(row){
56760        return this.store.getAt(row);
56761     },
56762
56763     isEditableValue: function(val){
56764         if(val && val instanceof Date){
56765             return true;
56766         }else if(typeof val == 'object' || typeof val == 'function'){
56767             return false;
56768         }
56769         return true;
56770     },
56771
56772     setValue : function(prop, value){
56773         this.source[prop] = value;
56774         this.store.getById(prop).set('value', value);
56775     },
56776
56777     getSource : function(){
56778         return this.source;
56779     }
56780 });
56781
56782 Roo.grid.PropertyColumnModel = function(grid, store){
56783     this.grid = grid;
56784     var g = Roo.grid;
56785     g.PropertyColumnModel.superclass.constructor.call(this, [
56786         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56787         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56788     ]);
56789     this.store = store;
56790     this.bselect = Roo.DomHelper.append(document.body, {
56791         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56792             {tag: 'option', value: 'true', html: 'true'},
56793             {tag: 'option', value: 'false', html: 'false'}
56794         ]
56795     });
56796     Roo.id(this.bselect);
56797     var f = Roo.form;
56798     this.editors = {
56799         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56800         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56801         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56802         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56803         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56804     };
56805     this.renderCellDelegate = this.renderCell.createDelegate(this);
56806     this.renderPropDelegate = this.renderProp.createDelegate(this);
56807 };
56808
56809 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56810     
56811     
56812     nameText : 'Name',
56813     valueText : 'Value',
56814     
56815     dateFormat : 'm/j/Y',
56816     
56817     
56818     renderDate : function(dateVal){
56819         return dateVal.dateFormat(this.dateFormat);
56820     },
56821
56822     renderBool : function(bVal){
56823         return bVal ? 'true' : 'false';
56824     },
56825
56826     isCellEditable : function(colIndex, rowIndex){
56827         return colIndex == 1;
56828     },
56829
56830     getRenderer : function(col){
56831         return col == 1 ?
56832             this.renderCellDelegate : this.renderPropDelegate;
56833     },
56834
56835     renderProp : function(v){
56836         return this.getPropertyName(v);
56837     },
56838
56839     renderCell : function(val){
56840         var rv = val;
56841         if(val instanceof Date){
56842             rv = this.renderDate(val);
56843         }else if(typeof val == 'boolean'){
56844             rv = this.renderBool(val);
56845         }
56846         return Roo.util.Format.htmlEncode(rv);
56847     },
56848
56849     getPropertyName : function(name){
56850         var pn = this.grid.propertyNames;
56851         return pn && pn[name] ? pn[name] : name;
56852     },
56853
56854     getCellEditor : function(colIndex, rowIndex){
56855         var p = this.store.getProperty(rowIndex);
56856         var n = p.data['name'], val = p.data['value'];
56857         
56858         if(typeof(this.grid.customEditors[n]) == 'string'){
56859             return this.editors[this.grid.customEditors[n]];
56860         }
56861         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56862             return this.grid.customEditors[n];
56863         }
56864         if(val instanceof Date){
56865             return this.editors['date'];
56866         }else if(typeof val == 'number'){
56867             return this.editors['number'];
56868         }else if(typeof val == 'boolean'){
56869             return this.editors['boolean'];
56870         }else{
56871             return this.editors['string'];
56872         }
56873     }
56874 });
56875
56876 /**
56877  * @class Roo.grid.PropertyGrid
56878  * @extends Roo.grid.EditorGrid
56879  * This class represents the  interface of a component based property grid control.
56880  * <br><br>Usage:<pre><code>
56881  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56882       
56883  });
56884  // set any options
56885  grid.render();
56886  * </code></pre>
56887   
56888  * @constructor
56889  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56890  * The container MUST have some type of size defined for the grid to fill. The container will be
56891  * automatically set to position relative if it isn't already.
56892  * @param {Object} config A config object that sets properties on this grid.
56893  */
56894 Roo.grid.PropertyGrid = function(container, config){
56895     config = config || {};
56896     var store = new Roo.grid.PropertyStore(this);
56897     this.store = store;
56898     var cm = new Roo.grid.PropertyColumnModel(this, store);
56899     store.store.sort('name', 'ASC');
56900     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56901         ds: store.store,
56902         cm: cm,
56903         enableColLock:false,
56904         enableColumnMove:false,
56905         stripeRows:false,
56906         trackMouseOver: false,
56907         clicksToEdit:1
56908     }, config));
56909     this.getGridEl().addClass('x-props-grid');
56910     this.lastEditRow = null;
56911     this.on('columnresize', this.onColumnResize, this);
56912     this.addEvents({
56913          /**
56914              * @event beforepropertychange
56915              * Fires before a property changes (return false to stop?)
56916              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56917              * @param {String} id Record Id
56918              * @param {String} newval New Value
56919          * @param {String} oldval Old Value
56920              */
56921         "beforepropertychange": true,
56922         /**
56923              * @event propertychange
56924              * Fires after a property changes
56925              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56926              * @param {String} id Record Id
56927              * @param {String} newval New Value
56928          * @param {String} oldval Old Value
56929              */
56930         "propertychange": true
56931     });
56932     this.customEditors = this.customEditors || {};
56933 };
56934 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56935     
56936      /**
56937      * @cfg {Object} customEditors map of colnames=> custom editors.
56938      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56939      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56940      * false disables editing of the field.
56941          */
56942     
56943       /**
56944      * @cfg {Object} propertyNames map of property Names to their displayed value
56945          */
56946     
56947     render : function(){
56948         Roo.grid.PropertyGrid.superclass.render.call(this);
56949         this.autoSize.defer(100, this);
56950     },
56951
56952     autoSize : function(){
56953         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56954         if(this.view){
56955             this.view.fitColumns();
56956         }
56957     },
56958
56959     onColumnResize : function(){
56960         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56961         this.autoSize();
56962     },
56963     /**
56964      * Sets the data for the Grid
56965      * accepts a Key => Value object of all the elements avaiable.
56966      * @param {Object} data  to appear in grid.
56967      */
56968     setSource : function(source){
56969         this.store.setSource(source);
56970         //this.autoSize();
56971     },
56972     /**
56973      * Gets all the data from the grid.
56974      * @return {Object} data  data stored in grid
56975      */
56976     getSource : function(){
56977         return this.store.getSource();
56978     }
56979 });/*
56980   
56981  * Licence LGPL
56982  
56983  */
56984  
56985 /**
56986  * @class Roo.grid.Calendar
56987  * @extends Roo.util.Grid
56988  * This class extends the Grid to provide a calendar widget
56989  * <br><br>Usage:<pre><code>
56990  var grid = new Roo.grid.Calendar("my-container-id", {
56991      ds: myDataStore,
56992      cm: myColModel,
56993      selModel: mySelectionModel,
56994      autoSizeColumns: true,
56995      monitorWindowResize: false,
56996      trackMouseOver: true
56997      eventstore : real data store..
56998  });
56999  // set any options
57000  grid.render();
57001   
57002   * @constructor
57003  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57004  * The container MUST have some type of size defined for the grid to fill. The container will be
57005  * automatically set to position relative if it isn't already.
57006  * @param {Object} config A config object that sets properties on this grid.
57007  */
57008 Roo.grid.Calendar = function(container, config){
57009         // initialize the container
57010         this.container = Roo.get(container);
57011         this.container.update("");
57012         this.container.setStyle("overflow", "hidden");
57013     this.container.addClass('x-grid-container');
57014
57015     this.id = this.container.id;
57016
57017     Roo.apply(this, config);
57018     // check and correct shorthanded configs
57019     
57020     var rows = [];
57021     var d =1;
57022     for (var r = 0;r < 6;r++) {
57023         
57024         rows[r]=[];
57025         for (var c =0;c < 7;c++) {
57026             rows[r][c]= '';
57027         }
57028     }
57029     if (this.eventStore) {
57030         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57031         this.eventStore.on('load',this.onLoad, this);
57032         this.eventStore.on('beforeload',this.clearEvents, this);
57033          
57034     }
57035     
57036     this.dataSource = new Roo.data.Store({
57037             proxy: new Roo.data.MemoryProxy(rows),
57038             reader: new Roo.data.ArrayReader({}, [
57039                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57040     });
57041
57042     this.dataSource.load();
57043     this.ds = this.dataSource;
57044     this.ds.xmodule = this.xmodule || false;
57045     
57046     
57047     var cellRender = function(v,x,r)
57048     {
57049         return String.format(
57050             '<div class="fc-day  fc-widget-content"><div>' +
57051                 '<div class="fc-event-container"></div>' +
57052                 '<div class="fc-day-number">{0}</div>'+
57053                 
57054                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57055             '</div></div>', v);
57056     
57057     }
57058     
57059     
57060     this.colModel = new Roo.grid.ColumnModel( [
57061         {
57062             xtype: 'ColumnModel',
57063             xns: Roo.grid,
57064             dataIndex : 'weekday0',
57065             header : 'Sunday',
57066             renderer : cellRender
57067         },
57068         {
57069             xtype: 'ColumnModel',
57070             xns: Roo.grid,
57071             dataIndex : 'weekday1',
57072             header : 'Monday',
57073             renderer : cellRender
57074         },
57075         {
57076             xtype: 'ColumnModel',
57077             xns: Roo.grid,
57078             dataIndex : 'weekday2',
57079             header : 'Tuesday',
57080             renderer : cellRender
57081         },
57082         {
57083             xtype: 'ColumnModel',
57084             xns: Roo.grid,
57085             dataIndex : 'weekday3',
57086             header : 'Wednesday',
57087             renderer : cellRender
57088         },
57089         {
57090             xtype: 'ColumnModel',
57091             xns: Roo.grid,
57092             dataIndex : 'weekday4',
57093             header : 'Thursday',
57094             renderer : cellRender
57095         },
57096         {
57097             xtype: 'ColumnModel',
57098             xns: Roo.grid,
57099             dataIndex : 'weekday5',
57100             header : 'Friday',
57101             renderer : cellRender
57102         },
57103         {
57104             xtype: 'ColumnModel',
57105             xns: Roo.grid,
57106             dataIndex : 'weekday6',
57107             header : 'Saturday',
57108             renderer : cellRender
57109         }
57110     ]);
57111     this.cm = this.colModel;
57112     this.cm.xmodule = this.xmodule || false;
57113  
57114         
57115           
57116     //this.selModel = new Roo.grid.CellSelectionModel();
57117     //this.sm = this.selModel;
57118     //this.selModel.init(this);
57119     
57120     
57121     if(this.width){
57122         this.container.setWidth(this.width);
57123     }
57124
57125     if(this.height){
57126         this.container.setHeight(this.height);
57127     }
57128     /** @private */
57129         this.addEvents({
57130         // raw events
57131         /**
57132          * @event click
57133          * The raw click event for the entire grid.
57134          * @param {Roo.EventObject} e
57135          */
57136         "click" : true,
57137         /**
57138          * @event dblclick
57139          * The raw dblclick event for the entire grid.
57140          * @param {Roo.EventObject} e
57141          */
57142         "dblclick" : true,
57143         /**
57144          * @event contextmenu
57145          * The raw contextmenu event for the entire grid.
57146          * @param {Roo.EventObject} e
57147          */
57148         "contextmenu" : true,
57149         /**
57150          * @event mousedown
57151          * The raw mousedown event for the entire grid.
57152          * @param {Roo.EventObject} e
57153          */
57154         "mousedown" : true,
57155         /**
57156          * @event mouseup
57157          * The raw mouseup event for the entire grid.
57158          * @param {Roo.EventObject} e
57159          */
57160         "mouseup" : true,
57161         /**
57162          * @event mouseover
57163          * The raw mouseover event for the entire grid.
57164          * @param {Roo.EventObject} e
57165          */
57166         "mouseover" : true,
57167         /**
57168          * @event mouseout
57169          * The raw mouseout event for the entire grid.
57170          * @param {Roo.EventObject} e
57171          */
57172         "mouseout" : true,
57173         /**
57174          * @event keypress
57175          * The raw keypress event for the entire grid.
57176          * @param {Roo.EventObject} e
57177          */
57178         "keypress" : true,
57179         /**
57180          * @event keydown
57181          * The raw keydown event for the entire grid.
57182          * @param {Roo.EventObject} e
57183          */
57184         "keydown" : true,
57185
57186         // custom events
57187
57188         /**
57189          * @event cellclick
57190          * Fires when a cell is clicked
57191          * @param {Grid} this
57192          * @param {Number} rowIndex
57193          * @param {Number} columnIndex
57194          * @param {Roo.EventObject} e
57195          */
57196         "cellclick" : true,
57197         /**
57198          * @event celldblclick
57199          * Fires when a cell is double clicked
57200          * @param {Grid} this
57201          * @param {Number} rowIndex
57202          * @param {Number} columnIndex
57203          * @param {Roo.EventObject} e
57204          */
57205         "celldblclick" : true,
57206         /**
57207          * @event rowclick
57208          * Fires when a row is clicked
57209          * @param {Grid} this
57210          * @param {Number} rowIndex
57211          * @param {Roo.EventObject} e
57212          */
57213         "rowclick" : true,
57214         /**
57215          * @event rowdblclick
57216          * Fires when a row is double clicked
57217          * @param {Grid} this
57218          * @param {Number} rowIndex
57219          * @param {Roo.EventObject} e
57220          */
57221         "rowdblclick" : true,
57222         /**
57223          * @event headerclick
57224          * Fires when a header is clicked
57225          * @param {Grid} this
57226          * @param {Number} columnIndex
57227          * @param {Roo.EventObject} e
57228          */
57229         "headerclick" : true,
57230         /**
57231          * @event headerdblclick
57232          * Fires when a header cell is double clicked
57233          * @param {Grid} this
57234          * @param {Number} columnIndex
57235          * @param {Roo.EventObject} e
57236          */
57237         "headerdblclick" : true,
57238         /**
57239          * @event rowcontextmenu
57240          * Fires when a row is right clicked
57241          * @param {Grid} this
57242          * @param {Number} rowIndex
57243          * @param {Roo.EventObject} e
57244          */
57245         "rowcontextmenu" : true,
57246         /**
57247          * @event cellcontextmenu
57248          * Fires when a cell is right clicked
57249          * @param {Grid} this
57250          * @param {Number} rowIndex
57251          * @param {Number} cellIndex
57252          * @param {Roo.EventObject} e
57253          */
57254          "cellcontextmenu" : true,
57255         /**
57256          * @event headercontextmenu
57257          * Fires when a header is right clicked
57258          * @param {Grid} this
57259          * @param {Number} columnIndex
57260          * @param {Roo.EventObject} e
57261          */
57262         "headercontextmenu" : true,
57263         /**
57264          * @event bodyscroll
57265          * Fires when the body element is scrolled
57266          * @param {Number} scrollLeft
57267          * @param {Number} scrollTop
57268          */
57269         "bodyscroll" : true,
57270         /**
57271          * @event columnresize
57272          * Fires when the user resizes a column
57273          * @param {Number} columnIndex
57274          * @param {Number} newSize
57275          */
57276         "columnresize" : true,
57277         /**
57278          * @event columnmove
57279          * Fires when the user moves a column
57280          * @param {Number} oldIndex
57281          * @param {Number} newIndex
57282          */
57283         "columnmove" : true,
57284         /**
57285          * @event startdrag
57286          * Fires when row(s) start being dragged
57287          * @param {Grid} this
57288          * @param {Roo.GridDD} dd The drag drop object
57289          * @param {event} e The raw browser event
57290          */
57291         "startdrag" : true,
57292         /**
57293          * @event enddrag
57294          * Fires when a drag operation is complete
57295          * @param {Grid} this
57296          * @param {Roo.GridDD} dd The drag drop object
57297          * @param {event} e The raw browser event
57298          */
57299         "enddrag" : true,
57300         /**
57301          * @event dragdrop
57302          * Fires when dragged row(s) are dropped on a valid DD target
57303          * @param {Grid} this
57304          * @param {Roo.GridDD} dd The drag drop object
57305          * @param {String} targetId The target drag drop object
57306          * @param {event} e The raw browser event
57307          */
57308         "dragdrop" : true,
57309         /**
57310          * @event dragover
57311          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57312          * @param {Grid} this
57313          * @param {Roo.GridDD} dd The drag drop object
57314          * @param {String} targetId The target drag drop object
57315          * @param {event} e The raw browser event
57316          */
57317         "dragover" : true,
57318         /**
57319          * @event dragenter
57320          *  Fires when the dragged row(s) first cross another DD target while being dragged
57321          * @param {Grid} this
57322          * @param {Roo.GridDD} dd The drag drop object
57323          * @param {String} targetId The target drag drop object
57324          * @param {event} e The raw browser event
57325          */
57326         "dragenter" : true,
57327         /**
57328          * @event dragout
57329          * Fires when the dragged row(s) leave another DD target while being dragged
57330          * @param {Grid} this
57331          * @param {Roo.GridDD} dd The drag drop object
57332          * @param {String} targetId The target drag drop object
57333          * @param {event} e The raw browser event
57334          */
57335         "dragout" : true,
57336         /**
57337          * @event rowclass
57338          * Fires when a row is rendered, so you can change add a style to it.
57339          * @param {GridView} gridview   The grid view
57340          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57341          */
57342         'rowclass' : true,
57343
57344         /**
57345          * @event render
57346          * Fires when the grid is rendered
57347          * @param {Grid} grid
57348          */
57349         'render' : true,
57350             /**
57351              * @event select
57352              * Fires when a date is selected
57353              * @param {DatePicker} this
57354              * @param {Date} date The selected date
57355              */
57356         'select': true,
57357         /**
57358              * @event monthchange
57359              * Fires when the displayed month changes 
57360              * @param {DatePicker} this
57361              * @param {Date} date The selected month
57362              */
57363         'monthchange': true,
57364         /**
57365              * @event evententer
57366              * Fires when mouse over an event
57367              * @param {Calendar} this
57368              * @param {event} Event
57369              */
57370         'evententer': true,
57371         /**
57372              * @event eventleave
57373              * Fires when the mouse leaves an
57374              * @param {Calendar} this
57375              * @param {event}
57376              */
57377         'eventleave': true,
57378         /**
57379              * @event eventclick
57380              * Fires when the mouse click an
57381              * @param {Calendar} this
57382              * @param {event}
57383              */
57384         'eventclick': true,
57385         /**
57386              * @event eventrender
57387              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57388              * @param {Calendar} this
57389              * @param {data} data to be modified
57390              */
57391         'eventrender': true
57392         
57393     });
57394
57395     Roo.grid.Grid.superclass.constructor.call(this);
57396     this.on('render', function() {
57397         this.view.el.addClass('x-grid-cal'); 
57398         
57399         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57400
57401     },this);
57402     
57403     if (!Roo.grid.Calendar.style) {
57404         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57405             
57406             
57407             '.x-grid-cal .x-grid-col' :  {
57408                 height: 'auto !important',
57409                 'vertical-align': 'top'
57410             },
57411             '.x-grid-cal  .fc-event-hori' : {
57412                 height: '14px'
57413             }
57414              
57415             
57416         }, Roo.id());
57417     }
57418
57419     
57420     
57421 };
57422 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57423     /**
57424      * @cfg {Store} eventStore The store that loads events.
57425      */
57426     eventStore : 25,
57427
57428      
57429     activeDate : false,
57430     startDay : 0,
57431     autoWidth : true,
57432     monitorWindowResize : false,
57433
57434     
57435     resizeColumns : function() {
57436         var col = (this.view.el.getWidth() / 7) - 3;
57437         // loop through cols, and setWidth
57438         for(var i =0 ; i < 7 ; i++){
57439             this.cm.setColumnWidth(i, col);
57440         }
57441     },
57442      setDate :function(date) {
57443         
57444         Roo.log('setDate?');
57445         
57446         this.resizeColumns();
57447         var vd = this.activeDate;
57448         this.activeDate = date;
57449 //        if(vd && this.el){
57450 //            var t = date.getTime();
57451 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57452 //                Roo.log('using add remove');
57453 //                
57454 //                this.fireEvent('monthchange', this, date);
57455 //                
57456 //                this.cells.removeClass("fc-state-highlight");
57457 //                this.cells.each(function(c){
57458 //                   if(c.dateValue == t){
57459 //                       c.addClass("fc-state-highlight");
57460 //                       setTimeout(function(){
57461 //                            try{c.dom.firstChild.focus();}catch(e){}
57462 //                       }, 50);
57463 //                       return false;
57464 //                   }
57465 //                   return true;
57466 //                });
57467 //                return;
57468 //            }
57469 //        }
57470         
57471         var days = date.getDaysInMonth();
57472         
57473         var firstOfMonth = date.getFirstDateOfMonth();
57474         var startingPos = firstOfMonth.getDay()-this.startDay;
57475         
57476         if(startingPos < this.startDay){
57477             startingPos += 7;
57478         }
57479         
57480         var pm = date.add(Date.MONTH, -1);
57481         var prevStart = pm.getDaysInMonth()-startingPos;
57482 //        
57483         
57484         
57485         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57486         
57487         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57488         //this.cells.addClassOnOver('fc-state-hover');
57489         
57490         var cells = this.cells.elements;
57491         var textEls = this.textNodes;
57492         
57493         //Roo.each(cells, function(cell){
57494         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57495         //});
57496         
57497         days += startingPos;
57498
57499         // convert everything to numbers so it's fast
57500         var day = 86400000;
57501         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57502         //Roo.log(d);
57503         //Roo.log(pm);
57504         //Roo.log(prevStart);
57505         
57506         var today = new Date().clearTime().getTime();
57507         var sel = date.clearTime().getTime();
57508         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57509         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57510         var ddMatch = this.disabledDatesRE;
57511         var ddText = this.disabledDatesText;
57512         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57513         var ddaysText = this.disabledDaysText;
57514         var format = this.format;
57515         
57516         var setCellClass = function(cal, cell){
57517             
57518             //Roo.log('set Cell Class');
57519             cell.title = "";
57520             var t = d.getTime();
57521             
57522             //Roo.log(d);
57523             
57524             
57525             cell.dateValue = t;
57526             if(t == today){
57527                 cell.className += " fc-today";
57528                 cell.className += " fc-state-highlight";
57529                 cell.title = cal.todayText;
57530             }
57531             if(t == sel){
57532                 // disable highlight in other month..
57533                 cell.className += " fc-state-highlight";
57534                 
57535             }
57536             // disabling
57537             if(t < min) {
57538                 //cell.className = " fc-state-disabled";
57539                 cell.title = cal.minText;
57540                 return;
57541             }
57542             if(t > max) {
57543                 //cell.className = " fc-state-disabled";
57544                 cell.title = cal.maxText;
57545                 return;
57546             }
57547             if(ddays){
57548                 if(ddays.indexOf(d.getDay()) != -1){
57549                     // cell.title = ddaysText;
57550                    // cell.className = " fc-state-disabled";
57551                 }
57552             }
57553             if(ddMatch && format){
57554                 var fvalue = d.dateFormat(format);
57555                 if(ddMatch.test(fvalue)){
57556                     cell.title = ddText.replace("%0", fvalue);
57557                    cell.className = " fc-state-disabled";
57558                 }
57559             }
57560             
57561             if (!cell.initialClassName) {
57562                 cell.initialClassName = cell.dom.className;
57563             }
57564             
57565             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57566         };
57567
57568         var i = 0;
57569         
57570         for(; i < startingPos; i++) {
57571             cells[i].dayName =  (++prevStart);
57572             Roo.log(textEls[i]);
57573             d.setDate(d.getDate()+1);
57574             
57575             //cells[i].className = "fc-past fc-other-month";
57576             setCellClass(this, cells[i]);
57577         }
57578         
57579         var intDay = 0;
57580         
57581         for(; i < days; i++){
57582             intDay = i - startingPos + 1;
57583             cells[i].dayName =  (intDay);
57584             d.setDate(d.getDate()+1);
57585             
57586             cells[i].className = ''; // "x-date-active";
57587             setCellClass(this, cells[i]);
57588         }
57589         var extraDays = 0;
57590         
57591         for(; i < 42; i++) {
57592             //textEls[i].innerHTML = (++extraDays);
57593             
57594             d.setDate(d.getDate()+1);
57595             cells[i].dayName = (++extraDays);
57596             cells[i].className = "fc-future fc-other-month";
57597             setCellClass(this, cells[i]);
57598         }
57599         
57600         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57601         
57602         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57603         
57604         // this will cause all the cells to mis
57605         var rows= [];
57606         var i =0;
57607         for (var r = 0;r < 6;r++) {
57608             for (var c =0;c < 7;c++) {
57609                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57610             }    
57611         }
57612         
57613         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57614         for(i=0;i<cells.length;i++) {
57615             
57616             this.cells.elements[i].dayName = cells[i].dayName ;
57617             this.cells.elements[i].className = cells[i].className;
57618             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57619             this.cells.elements[i].title = cells[i].title ;
57620             this.cells.elements[i].dateValue = cells[i].dateValue ;
57621         }
57622         
57623         
57624         
57625         
57626         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57627         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57628         
57629         ////if(totalRows != 6){
57630             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57631            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57632        // }
57633         
57634         this.fireEvent('monthchange', this, date);
57635         
57636         
57637     },
57638  /**
57639      * Returns the grid's SelectionModel.
57640      * @return {SelectionModel}
57641      */
57642     getSelectionModel : function(){
57643         if(!this.selModel){
57644             this.selModel = new Roo.grid.CellSelectionModel();
57645         }
57646         return this.selModel;
57647     },
57648
57649     load: function() {
57650         this.eventStore.load()
57651         
57652         
57653         
57654     },
57655     
57656     findCell : function(dt) {
57657         dt = dt.clearTime().getTime();
57658         var ret = false;
57659         this.cells.each(function(c){
57660             //Roo.log("check " +c.dateValue + '?=' + dt);
57661             if(c.dateValue == dt){
57662                 ret = c;
57663                 return false;
57664             }
57665             return true;
57666         });
57667         
57668         return ret;
57669     },
57670     
57671     findCells : function(rec) {
57672         var s = rec.data.start_dt.clone().clearTime().getTime();
57673        // Roo.log(s);
57674         var e= rec.data.end_dt.clone().clearTime().getTime();
57675        // Roo.log(e);
57676         var ret = [];
57677         this.cells.each(function(c){
57678              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57679             
57680             if(c.dateValue > e){
57681                 return ;
57682             }
57683             if(c.dateValue < s){
57684                 return ;
57685             }
57686             ret.push(c);
57687         });
57688         
57689         return ret;    
57690     },
57691     
57692     findBestRow: function(cells)
57693     {
57694         var ret = 0;
57695         
57696         for (var i =0 ; i < cells.length;i++) {
57697             ret  = Math.max(cells[i].rows || 0,ret);
57698         }
57699         return ret;
57700         
57701     },
57702     
57703     
57704     addItem : function(rec)
57705     {
57706         // look for vertical location slot in
57707         var cells = this.findCells(rec);
57708         
57709         rec.row = this.findBestRow(cells);
57710         
57711         // work out the location.
57712         
57713         var crow = false;
57714         var rows = [];
57715         for(var i =0; i < cells.length; i++) {
57716             if (!crow) {
57717                 crow = {
57718                     start : cells[i],
57719                     end :  cells[i]
57720                 };
57721                 continue;
57722             }
57723             if (crow.start.getY() == cells[i].getY()) {
57724                 // on same row.
57725                 crow.end = cells[i];
57726                 continue;
57727             }
57728             // different row.
57729             rows.push(crow);
57730             crow = {
57731                 start: cells[i],
57732                 end : cells[i]
57733             };
57734             
57735         }
57736         
57737         rows.push(crow);
57738         rec.els = [];
57739         rec.rows = rows;
57740         rec.cells = cells;
57741         for (var i = 0; i < cells.length;i++) {
57742             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57743             
57744         }
57745         
57746         
57747     },
57748     
57749     clearEvents: function() {
57750         
57751         if (!this.eventStore.getCount()) {
57752             return;
57753         }
57754         // reset number of rows in cells.
57755         Roo.each(this.cells.elements, function(c){
57756             c.rows = 0;
57757         });
57758         
57759         this.eventStore.each(function(e) {
57760             this.clearEvent(e);
57761         },this);
57762         
57763     },
57764     
57765     clearEvent : function(ev)
57766     {
57767         if (ev.els) {
57768             Roo.each(ev.els, function(el) {
57769                 el.un('mouseenter' ,this.onEventEnter, this);
57770                 el.un('mouseleave' ,this.onEventLeave, this);
57771                 el.remove();
57772             },this);
57773             ev.els = [];
57774         }
57775     },
57776     
57777     
57778     renderEvent : function(ev,ctr) {
57779         if (!ctr) {
57780              ctr = this.view.el.select('.fc-event-container',true).first();
57781         }
57782         
57783          
57784         this.clearEvent(ev);
57785             //code
57786        
57787         
57788         
57789         ev.els = [];
57790         var cells = ev.cells;
57791         var rows = ev.rows;
57792         this.fireEvent('eventrender', this, ev);
57793         
57794         for(var i =0; i < rows.length; i++) {
57795             
57796             cls = '';
57797             if (i == 0) {
57798                 cls += ' fc-event-start';
57799             }
57800             if ((i+1) == rows.length) {
57801                 cls += ' fc-event-end';
57802             }
57803             
57804             //Roo.log(ev.data);
57805             // how many rows should it span..
57806             var cg = this.eventTmpl.append(ctr,Roo.apply({
57807                 fccls : cls
57808                 
57809             }, ev.data) , true);
57810             
57811             
57812             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57813             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57814             cg.on('click', this.onEventClick, this, ev);
57815             
57816             ev.els.push(cg);
57817             
57818             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57819             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57820             //Roo.log(cg);
57821              
57822             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57823             cg.setWidth(ebox.right - sbox.x -2);
57824         }
57825     },
57826     
57827     renderEvents: function()
57828     {   
57829         // first make sure there is enough space..
57830         
57831         if (!this.eventTmpl) {
57832             this.eventTmpl = new Roo.Template(
57833                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57834                     '<div class="fc-event-inner">' +
57835                         '<span class="fc-event-time">{time}</span>' +
57836                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57837                     '</div>' +
57838                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57839                 '</div>'
57840             );
57841                 
57842         }
57843                
57844         
57845         
57846         this.cells.each(function(c) {
57847             //Roo.log(c.select('.fc-day-content div',true).first());
57848             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57849         });
57850         
57851         var ctr = this.view.el.select('.fc-event-container',true).first();
57852         
57853         var cls;
57854         this.eventStore.each(function(ev){
57855             
57856             this.renderEvent(ev);
57857              
57858              
57859         }, this);
57860         this.view.layout();
57861         
57862     },
57863     
57864     onEventEnter: function (e, el,event,d) {
57865         this.fireEvent('evententer', this, el, event);
57866     },
57867     
57868     onEventLeave: function (e, el,event,d) {
57869         this.fireEvent('eventleave', this, el, event);
57870     },
57871     
57872     onEventClick: function (e, el,event,d) {
57873         this.fireEvent('eventclick', this, el, event);
57874     },
57875     
57876     onMonthChange: function () {
57877         this.store.load();
57878     },
57879     
57880     onLoad: function () {
57881         
57882         //Roo.log('calendar onload');
57883 //         
57884         if(this.eventStore.getCount() > 0){
57885             
57886            
57887             
57888             this.eventStore.each(function(d){
57889                 
57890                 
57891                 // FIXME..
57892                 var add =   d.data;
57893                 if (typeof(add.end_dt) == 'undefined')  {
57894                     Roo.log("Missing End time in calendar data: ");
57895                     Roo.log(d);
57896                     return;
57897                 }
57898                 if (typeof(add.start_dt) == 'undefined')  {
57899                     Roo.log("Missing Start time in calendar data: ");
57900                     Roo.log(d);
57901                     return;
57902                 }
57903                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57904                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57905                 add.id = add.id || d.id;
57906                 add.title = add.title || '??';
57907                 
57908                 this.addItem(d);
57909                 
57910              
57911             },this);
57912         }
57913         
57914         this.renderEvents();
57915     }
57916     
57917
57918 });
57919 /*
57920  grid : {
57921                 xtype: 'Grid',
57922                 xns: Roo.grid,
57923                 listeners : {
57924                     render : function ()
57925                     {
57926                         _this.grid = this;
57927                         
57928                         if (!this.view.el.hasClass('course-timesheet')) {
57929                             this.view.el.addClass('course-timesheet');
57930                         }
57931                         if (this.tsStyle) {
57932                             this.ds.load({});
57933                             return; 
57934                         }
57935                         Roo.log('width');
57936                         Roo.log(_this.grid.view.el.getWidth());
57937                         
57938                         
57939                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57940                             '.course-timesheet .x-grid-row' : {
57941                                 height: '80px'
57942                             },
57943                             '.x-grid-row td' : {
57944                                 'vertical-align' : 0
57945                             },
57946                             '.course-edit-link' : {
57947                                 'color' : 'blue',
57948                                 'text-overflow' : 'ellipsis',
57949                                 'overflow' : 'hidden',
57950                                 'white-space' : 'nowrap',
57951                                 'cursor' : 'pointer'
57952                             },
57953                             '.sub-link' : {
57954                                 'color' : 'green'
57955                             },
57956                             '.de-act-sup-link' : {
57957                                 'color' : 'purple',
57958                                 'text-decoration' : 'line-through'
57959                             },
57960                             '.de-act-link' : {
57961                                 'color' : 'red',
57962                                 'text-decoration' : 'line-through'
57963                             },
57964                             '.course-timesheet .course-highlight' : {
57965                                 'border-top-style': 'dashed !important',
57966                                 'border-bottom-bottom': 'dashed !important'
57967                             },
57968                             '.course-timesheet .course-item' : {
57969                                 'font-family'   : 'tahoma, arial, helvetica',
57970                                 'font-size'     : '11px',
57971                                 'overflow'      : 'hidden',
57972                                 'padding-left'  : '10px',
57973                                 'padding-right' : '10px',
57974                                 'padding-top' : '10px' 
57975                             }
57976                             
57977                         }, Roo.id());
57978                                 this.ds.load({});
57979                     }
57980                 },
57981                 autoWidth : true,
57982                 monitorWindowResize : false,
57983                 cellrenderer : function(v,x,r)
57984                 {
57985                     return v;
57986                 },
57987                 sm : {
57988                     xtype: 'CellSelectionModel',
57989                     xns: Roo.grid
57990                 },
57991                 dataSource : {
57992                     xtype: 'Store',
57993                     xns: Roo.data,
57994                     listeners : {
57995                         beforeload : function (_self, options)
57996                         {
57997                             options.params = options.params || {};
57998                             options.params._month = _this.monthField.getValue();
57999                             options.params.limit = 9999;
58000                             options.params['sort'] = 'when_dt';    
58001                             options.params['dir'] = 'ASC';    
58002                             this.proxy.loadResponse = this.loadResponse;
58003                             Roo.log("load?");
58004                             //this.addColumns();
58005                         },
58006                         load : function (_self, records, options)
58007                         {
58008                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58009                                 // if you click on the translation.. you can edit it...
58010                                 var el = Roo.get(this);
58011                                 var id = el.dom.getAttribute('data-id');
58012                                 var d = el.dom.getAttribute('data-date');
58013                                 var t = el.dom.getAttribute('data-time');
58014                                 //var id = this.child('span').dom.textContent;
58015                                 
58016                                 //Roo.log(this);
58017                                 Pman.Dialog.CourseCalendar.show({
58018                                     id : id,
58019                                     when_d : d,
58020                                     when_t : t,
58021                                     productitem_active : id ? 1 : 0
58022                                 }, function() {
58023                                     _this.grid.ds.load({});
58024                                 });
58025                            
58026                            });
58027                            
58028                            _this.panel.fireEvent('resize', [ '', '' ]);
58029                         }
58030                     },
58031                     loadResponse : function(o, success, response){
58032                             // this is overridden on before load..
58033                             
58034                             Roo.log("our code?");       
58035                             //Roo.log(success);
58036                             //Roo.log(response)
58037                             delete this.activeRequest;
58038                             if(!success){
58039                                 this.fireEvent("loadexception", this, o, response);
58040                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58041                                 return;
58042                             }
58043                             var result;
58044                             try {
58045                                 result = o.reader.read(response);
58046                             }catch(e){
58047                                 Roo.log("load exception?");
58048                                 this.fireEvent("loadexception", this, o, response, e);
58049                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58050                                 return;
58051                             }
58052                             Roo.log("ready...");        
58053                             // loop through result.records;
58054                             // and set this.tdate[date] = [] << array of records..
58055                             _this.tdata  = {};
58056                             Roo.each(result.records, function(r){
58057                                 //Roo.log(r.data);
58058                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58059                                     _this.tdata[r.data.when_dt.format('j')] = [];
58060                                 }
58061                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58062                             });
58063                             
58064                             //Roo.log(_this.tdata);
58065                             
58066                             result.records = [];
58067                             result.totalRecords = 6;
58068                     
58069                             // let's generate some duumy records for the rows.
58070                             //var st = _this.dateField.getValue();
58071                             
58072                             // work out monday..
58073                             //st = st.add(Date.DAY, -1 * st.format('w'));
58074                             
58075                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58076                             
58077                             var firstOfMonth = date.getFirstDayOfMonth();
58078                             var days = date.getDaysInMonth();
58079                             var d = 1;
58080                             var firstAdded = false;
58081                             for (var i = 0; i < result.totalRecords ; i++) {
58082                                 //var d= st.add(Date.DAY, i);
58083                                 var row = {};
58084                                 var added = 0;
58085                                 for(var w = 0 ; w < 7 ; w++){
58086                                     if(!firstAdded && firstOfMonth != w){
58087                                         continue;
58088                                     }
58089                                     if(d > days){
58090                                         continue;
58091                                     }
58092                                     firstAdded = true;
58093                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58094                                     row['weekday'+w] = String.format(
58095                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58096                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58097                                                     d,
58098                                                     date.format('Y-m-')+dd
58099                                                 );
58100                                     added++;
58101                                     if(typeof(_this.tdata[d]) != 'undefined'){
58102                                         Roo.each(_this.tdata[d], function(r){
58103                                             var is_sub = '';
58104                                             var deactive = '';
58105                                             var id = r.id;
58106                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58107                                             if(r.parent_id*1>0){
58108                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58109                                                 id = r.parent_id;
58110                                             }
58111                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58112                                                 deactive = 'de-act-link';
58113                                             }
58114                                             
58115                                             row['weekday'+w] += String.format(
58116                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58117                                                     id, //0
58118                                                     r.product_id_name, //1
58119                                                     r.when_dt.format('h:ia'), //2
58120                                                     is_sub, //3
58121                                                     deactive, //4
58122                                                     desc // 5
58123                                             );
58124                                         });
58125                                     }
58126                                     d++;
58127                                 }
58128                                 
58129                                 // only do this if something added..
58130                                 if(added > 0){ 
58131                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58132                                 }
58133                                 
58134                                 
58135                                 // push it twice. (second one with an hour..
58136                                 
58137                             }
58138                             //Roo.log(result);
58139                             this.fireEvent("load", this, o, o.request.arg);
58140                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58141                         },
58142                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58143                     proxy : {
58144                         xtype: 'HttpProxy',
58145                         xns: Roo.data,
58146                         method : 'GET',
58147                         url : baseURL + '/Roo/Shop_course.php'
58148                     },
58149                     reader : {
58150                         xtype: 'JsonReader',
58151                         xns: Roo.data,
58152                         id : 'id',
58153                         fields : [
58154                             {
58155                                 'name': 'id',
58156                                 'type': 'int'
58157                             },
58158                             {
58159                                 'name': 'when_dt',
58160                                 'type': 'string'
58161                             },
58162                             {
58163                                 'name': 'end_dt',
58164                                 'type': 'string'
58165                             },
58166                             {
58167                                 'name': 'parent_id',
58168                                 'type': 'int'
58169                             },
58170                             {
58171                                 'name': 'product_id',
58172                                 'type': 'int'
58173                             },
58174                             {
58175                                 'name': 'productitem_id',
58176                                 'type': 'int'
58177                             },
58178                             {
58179                                 'name': 'guid',
58180                                 'type': 'int'
58181                             }
58182                         ]
58183                     }
58184                 },
58185                 toolbar : {
58186                     xtype: 'Toolbar',
58187                     xns: Roo,
58188                     items : [
58189                         {
58190                             xtype: 'Button',
58191                             xns: Roo.Toolbar,
58192                             listeners : {
58193                                 click : function (_self, e)
58194                                 {
58195                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58196                                     sd.setMonth(sd.getMonth()-1);
58197                                     _this.monthField.setValue(sd.format('Y-m-d'));
58198                                     _this.grid.ds.load({});
58199                                 }
58200                             },
58201                             text : "Back"
58202                         },
58203                         {
58204                             xtype: 'Separator',
58205                             xns: Roo.Toolbar
58206                         },
58207                         {
58208                             xtype: 'MonthField',
58209                             xns: Roo.form,
58210                             listeners : {
58211                                 render : function (_self)
58212                                 {
58213                                     _this.monthField = _self;
58214                                    // _this.monthField.set  today
58215                                 },
58216                                 select : function (combo, date)
58217                                 {
58218                                     _this.grid.ds.load({});
58219                                 }
58220                             },
58221                             value : (function() { return new Date(); })()
58222                         },
58223                         {
58224                             xtype: 'Separator',
58225                             xns: Roo.Toolbar
58226                         },
58227                         {
58228                             xtype: 'TextItem',
58229                             xns: Roo.Toolbar,
58230                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58231                         },
58232                         {
58233                             xtype: 'Fill',
58234                             xns: Roo.Toolbar
58235                         },
58236                         {
58237                             xtype: 'Button',
58238                             xns: Roo.Toolbar,
58239                             listeners : {
58240                                 click : function (_self, e)
58241                                 {
58242                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58243                                     sd.setMonth(sd.getMonth()+1);
58244                                     _this.monthField.setValue(sd.format('Y-m-d'));
58245                                     _this.grid.ds.load({});
58246                                 }
58247                             },
58248                             text : "Next"
58249                         }
58250                     ]
58251                 },
58252                  
58253             }
58254         };
58255         
58256         *//*
58257  * Based on:
58258  * Ext JS Library 1.1.1
58259  * Copyright(c) 2006-2007, Ext JS, LLC.
58260  *
58261  * Originally Released Under LGPL - original licence link has changed is not relivant.
58262  *
58263  * Fork - LGPL
58264  * <script type="text/javascript">
58265  */
58266  
58267 /**
58268  * @class Roo.LoadMask
58269  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58270  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58271  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58272  * element's UpdateManager load indicator and will be destroyed after the initial load.
58273  * @constructor
58274  * Create a new LoadMask
58275  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58276  * @param {Object} config The config object
58277  */
58278 Roo.LoadMask = function(el, config){
58279     this.el = Roo.get(el);
58280     Roo.apply(this, config);
58281     if(this.store){
58282         this.store.on('beforeload', this.onBeforeLoad, this);
58283         this.store.on('load', this.onLoad, this);
58284         this.store.on('loadexception', this.onLoadException, this);
58285         this.removeMask = false;
58286     }else{
58287         var um = this.el.getUpdateManager();
58288         um.showLoadIndicator = false; // disable the default indicator
58289         um.on('beforeupdate', this.onBeforeLoad, this);
58290         um.on('update', this.onLoad, this);
58291         um.on('failure', this.onLoad, this);
58292         this.removeMask = true;
58293     }
58294 };
58295
58296 Roo.LoadMask.prototype = {
58297     /**
58298      * @cfg {Boolean} removeMask
58299      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58300      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58301      */
58302     /**
58303      * @cfg {String} msg
58304      * The text to display in a centered loading message box (defaults to 'Loading...')
58305      */
58306     msg : 'Loading...',
58307     /**
58308      * @cfg {String} msgCls
58309      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58310      */
58311     msgCls : 'x-mask-loading',
58312
58313     /**
58314      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58315      * @type Boolean
58316      */
58317     disabled: false,
58318
58319     /**
58320      * Disables the mask to prevent it from being displayed
58321      */
58322     disable : function(){
58323        this.disabled = true;
58324     },
58325
58326     /**
58327      * Enables the mask so that it can be displayed
58328      */
58329     enable : function(){
58330         this.disabled = false;
58331     },
58332     
58333     onLoadException : function()
58334     {
58335         Roo.log(arguments);
58336         
58337         if (typeof(arguments[3]) != 'undefined') {
58338             Roo.MessageBox.alert("Error loading",arguments[3]);
58339         } 
58340         /*
58341         try {
58342             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58343                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58344             }   
58345         } catch(e) {
58346             
58347         }
58348         */
58349     
58350         
58351         
58352         this.el.unmask(this.removeMask);
58353     },
58354     // private
58355     onLoad : function()
58356     {
58357         this.el.unmask(this.removeMask);
58358     },
58359
58360     // private
58361     onBeforeLoad : function(){
58362         if(!this.disabled){
58363             this.el.mask(this.msg, this.msgCls);
58364         }
58365     },
58366
58367     // private
58368     destroy : function(){
58369         if(this.store){
58370             this.store.un('beforeload', this.onBeforeLoad, this);
58371             this.store.un('load', this.onLoad, this);
58372             this.store.un('loadexception', this.onLoadException, this);
58373         }else{
58374             var um = this.el.getUpdateManager();
58375             um.un('beforeupdate', this.onBeforeLoad, this);
58376             um.un('update', this.onLoad, this);
58377             um.un('failure', this.onLoad, this);
58378         }
58379     }
58380 };/*
58381  * Based on:
58382  * Ext JS Library 1.1.1
58383  * Copyright(c) 2006-2007, Ext JS, LLC.
58384  *
58385  * Originally Released Under LGPL - original licence link has changed is not relivant.
58386  *
58387  * Fork - LGPL
58388  * <script type="text/javascript">
58389  */
58390
58391
58392 /**
58393  * @class Roo.XTemplate
58394  * @extends Roo.Template
58395  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58396 <pre><code>
58397 var t = new Roo.XTemplate(
58398         '&lt;select name="{name}"&gt;',
58399                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58400         '&lt;/select&gt;'
58401 );
58402  
58403 // then append, applying the master template values
58404  </code></pre>
58405  *
58406  * Supported features:
58407  *
58408  *  Tags:
58409
58410 <pre><code>
58411       {a_variable} - output encoded.
58412       {a_variable.format:("Y-m-d")} - call a method on the variable
58413       {a_variable:raw} - unencoded output
58414       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58415       {a_variable:this.method_on_template(...)} - call a method on the template object.
58416  
58417 </code></pre>
58418  *  The tpl tag:
58419 <pre><code>
58420         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58421         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58422         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58423         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58424   
58425         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58426         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58427 </code></pre>
58428  *      
58429  */
58430 Roo.XTemplate = function()
58431 {
58432     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58433     if (this.html) {
58434         this.compile();
58435     }
58436 };
58437
58438
58439 Roo.extend(Roo.XTemplate, Roo.Template, {
58440
58441     /**
58442      * The various sub templates
58443      */
58444     tpls : false,
58445     /**
58446      *
58447      * basic tag replacing syntax
58448      * WORD:WORD()
58449      *
58450      * // you can fake an object call by doing this
58451      *  x.t:(test,tesT) 
58452      * 
58453      */
58454     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58455
58456     /**
58457      * compile the template
58458      *
58459      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58460      *
58461      */
58462     compile: function()
58463     {
58464         var s = this.html;
58465      
58466         s = ['<tpl>', s, '</tpl>'].join('');
58467     
58468         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58469             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58470             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58471             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58472             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58473             m,
58474             id     = 0,
58475             tpls   = [];
58476     
58477         while(true == !!(m = s.match(re))){
58478             var forMatch   = m[0].match(nameRe),
58479                 ifMatch   = m[0].match(ifRe),
58480                 execMatch   = m[0].match(execRe),
58481                 namedMatch   = m[0].match(namedRe),
58482                 
58483                 exp  = null, 
58484                 fn   = null,
58485                 exec = null,
58486                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58487                 
58488             if (ifMatch) {
58489                 // if - puts fn into test..
58490                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58491                 if(exp){
58492                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58493                 }
58494             }
58495             
58496             if (execMatch) {
58497                 // exec - calls a function... returns empty if true is  returned.
58498                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58499                 if(exp){
58500                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58501                 }
58502             }
58503             
58504             
58505             if (name) {
58506                 // for = 
58507                 switch(name){
58508                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58509                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58510                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58511                 }
58512             }
58513             var uid = namedMatch ? namedMatch[1] : id;
58514             
58515             
58516             tpls.push({
58517                 id:     namedMatch ? namedMatch[1] : id,
58518                 target: name,
58519                 exec:   exec,
58520                 test:   fn,
58521                 body:   m[1] || ''
58522             });
58523             if (namedMatch) {
58524                 s = s.replace(m[0], '');
58525             } else { 
58526                 s = s.replace(m[0], '{xtpl'+ id + '}');
58527             }
58528             ++id;
58529         }
58530         this.tpls = [];
58531         for(var i = tpls.length-1; i >= 0; --i){
58532             this.compileTpl(tpls[i]);
58533             this.tpls[tpls[i].id] = tpls[i];
58534         }
58535         this.master = tpls[tpls.length-1];
58536         return this;
58537     },
58538     /**
58539      * same as applyTemplate, except it's done to one of the subTemplates
58540      * when using named templates, you can do:
58541      *
58542      * var str = pl.applySubTemplate('your-name', values);
58543      *
58544      * 
58545      * @param {Number} id of the template
58546      * @param {Object} values to apply to template
58547      * @param {Object} parent (normaly the instance of this object)
58548      */
58549     applySubTemplate : function(id, values, parent)
58550     {
58551         
58552         
58553         var t = this.tpls[id];
58554         
58555         
58556         try { 
58557             if(t.test && !t.test.call(this, values, parent)){
58558                 return '';
58559             }
58560         } catch(e) {
58561             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58562             Roo.log(e.toString());
58563             Roo.log(t.test);
58564             return ''
58565         }
58566         try { 
58567             
58568             if(t.exec && t.exec.call(this, values, parent)){
58569                 return '';
58570             }
58571         } catch(e) {
58572             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58573             Roo.log(e.toString());
58574             Roo.log(t.exec);
58575             return ''
58576         }
58577         try {
58578             var vs = t.target ? t.target.call(this, values, parent) : values;
58579             parent = t.target ? values : parent;
58580             if(t.target && vs instanceof Array){
58581                 var buf = [];
58582                 for(var i = 0, len = vs.length; i < len; i++){
58583                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58584                 }
58585                 return buf.join('');
58586             }
58587             return t.compiled.call(this, vs, parent);
58588         } catch (e) {
58589             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58590             Roo.log(e.toString());
58591             Roo.log(t.compiled);
58592             return '';
58593         }
58594     },
58595
58596     compileTpl : function(tpl)
58597     {
58598         var fm = Roo.util.Format;
58599         var useF = this.disableFormats !== true;
58600         var sep = Roo.isGecko ? "+" : ",";
58601         var undef = function(str) {
58602             Roo.log("Property not found :"  + str);
58603             return '';
58604         };
58605         
58606         var fn = function(m, name, format, args)
58607         {
58608             //Roo.log(arguments);
58609             args = args ? args.replace(/\\'/g,"'") : args;
58610             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58611             if (typeof(format) == 'undefined') {
58612                 format= 'htmlEncode';
58613             }
58614             if (format == 'raw' ) {
58615                 format = false;
58616             }
58617             
58618             if(name.substr(0, 4) == 'xtpl'){
58619                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58620             }
58621             
58622             // build an array of options to determine if value is undefined..
58623             
58624             // basically get 'xxxx.yyyy' then do
58625             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58626             //    (function () { Roo.log("Property not found"); return ''; })() :
58627             //    ......
58628             
58629             var udef_ar = [];
58630             var lookfor = '';
58631             Roo.each(name.split('.'), function(st) {
58632                 lookfor += (lookfor.length ? '.': '') + st;
58633                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58634             });
58635             
58636             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58637             
58638             
58639             if(format && useF){
58640                 
58641                 args = args ? ',' + args : "";
58642                  
58643                 if(format.substr(0, 5) != "this."){
58644                     format = "fm." + format + '(';
58645                 }else{
58646                     format = 'this.call("'+ format.substr(5) + '", ';
58647                     args = ", values";
58648                 }
58649                 
58650                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58651             }
58652              
58653             if (args.length) {
58654                 // called with xxyx.yuu:(test,test)
58655                 // change to ()
58656                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58657             }
58658             // raw.. - :raw modifier..
58659             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58660             
58661         };
58662         var body;
58663         // branched to use + in gecko and [].join() in others
58664         if(Roo.isGecko){
58665             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58666                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58667                     "';};};";
58668         }else{
58669             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58670             body.push(tpl.body.replace(/(\r\n|\n)/g,
58671                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58672             body.push("'].join('');};};");
58673             body = body.join('');
58674         }
58675         
58676         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58677        
58678         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58679         eval(body);
58680         
58681         return this;
58682     },
58683
58684     applyTemplate : function(values){
58685         return this.master.compiled.call(this, values, {});
58686         //var s = this.subs;
58687     },
58688
58689     apply : function(){
58690         return this.applyTemplate.apply(this, arguments);
58691     }
58692
58693  });
58694
58695 Roo.XTemplate.from = function(el){
58696     el = Roo.getDom(el);
58697     return new Roo.XTemplate(el.value || el.innerHTML);
58698 };